"Andre Vieira (lists)" <andre.simoesdiasvie...@arm.com> writes: > diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi > index > 19e89ae502bc2f51db64667b236c1cb669718b02..3b0e4e0875b4392ab6833568b207580ef597a98f > 100644 > --- a/gcc/doc/md.texi > +++ b/gcc/doc/md.texi > @@ -6191,6 +6191,15 @@ operands; otherwise, it may not. > > This pattern is not allowed to @code{FAIL}. > > +@cindex @code{ftrunc@var{m}@var{n}2} instruction pattern > +@item @samp{ftrunc@var{m}@var{n}2} > +Truncate operand 1 to a @var{n} mode signed integer, towards zero, and store > +the result in operand 0. Both operands have mode @var{m}, which is a scalar > or > +vector floating-point mode. An exception must be raised if operand 1 does > not > +fit in a @var{n} mode signed integer as it would have if the truncation > +happened through separate floating point to integer conversion. > + > +
Nit: just one blank line here. > @cindex @code{round@var{m}2} instruction pattern > @item @samp{round@var{m}2} > Round operand 1 to the nearest integer, rounding away from zero in the > diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi > index > 6095a35cd4565fdb7d758104e80fe6411230f758..a56bbb775572fa72379854f90a01ad543557e29a > 100644 > --- a/gcc/doc/sourcebuild.texi > +++ b/gcc/doc/sourcebuild.texi > @@ -2286,6 +2286,10 @@ Like @code{aarch64_sve_hw}, but also test for an exact > hardware vector length. > @item aarch64_fjcvtzs_hw > AArch64 target that is able to generate and execute armv8.3-a FJCVTZS > instruction. > + > +@item aarch64_frintzx_ok > +AArch64 target that is able to generate the Armv8.5-a FRINT32Z, FRINT64Z, > +FRINT32X and FRINT64X instructions. > @end table > > @subsubsection MIPS-specific attributes > diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c > index > b24102a5990bea4cbb102069f7a6df497fc81ebf..9047b486f41948059a7a7f1ccc4032410a369139 > 100644 > --- a/gcc/internal-fn.c > +++ b/gcc/internal-fn.c > @@ -130,6 +130,7 @@ init_internal_fns () > #define fold_left_direct { 1, 1, false } > #define mask_fold_left_direct { 1, 1, false } > #define check_ptrs_direct { 0, 0, false } > +#define ftrunc_int_direct { 0, 1, true } > > const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = { > #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) not_direct, > @@ -156,6 +157,29 @@ get_multi_vector_move (tree array_type, convert_optab > optab) > return convert_optab_handler (optab, imode, vmode); > } > > +/* Expand FTRUNC_INT call STMT using optab OPTAB. */ > + > +static void > +expand_ftrunc_int_optab_fn (internal_fn, gcall *stmt, convert_optab optab) > +{ > + class expand_operand ops[2]; > + tree lhs, float_type, int_type; > + rtx target, op; > + > + lhs = gimple_call_lhs (stmt); > + target = expand_normal (lhs); > + op = expand_normal (gimple_call_arg (stmt, 0)); > + > + float_type = TREE_TYPE (lhs); > + int_type = element_type (gimple_call_arg (stmt, 1)); Sorry for the run-around, but now that we don't (need to) vectorise the second argument, I think we can drop this element_type. That in turn means that… > + > + create_output_operand (&ops[0], target, TYPE_MODE (float_type)); > + create_input_operand (&ops[1], op, TYPE_MODE (float_type)); > + > + expand_insn (convert_optab_handler (optab, TYPE_MODE (float_type), > + TYPE_MODE (int_type)), 2, ops); > +} > + > /* Expand LOAD_LANES call STMT using optab OPTAB. */ > > static void > @@ -3747,6 +3771,15 @@ multi_vector_optab_supported_p (convert_optab optab, > tree_pair types, > != CODE_FOR_nothing); > } > > +static bool > +direct_ftrunc_int_optab_supported_p (convert_optab optab, tree_pair types, > + optimization_type opt_type) > +{ > + return (convert_optab_handler (optab, TYPE_MODE (types.first), > + TYPE_MODE (element_type (types.second)), > + opt_type) != CODE_FOR_nothing); > +} > + …this can use convert_optab_supported_p. > diff --git a/gcc/testsuite/gcc.target/aarch64/frintnz.c > b/gcc/testsuite/gcc.target/aarch64/frintnz.c > new file mode 100644 > index > 0000000000000000000000000000000000000000..008e1cf9f4a1b0148128c65c9ea0d1bb111467b7 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/frintnz.c > @@ -0,0 +1,91 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -march=armv8.5-a" } */ > +/* { dg-require-effective-target aarch64_frintnzx_ok } */ Is this just a cut-&-pasto from a run test? If not, why do we need both this and the dg-options? It feels like one on its own should be enough, with the dg-options being better. The test looks OK without this line. > +/* { dg-final { check-function-bodies "**" "" } } */ > + > +/* > +** f1: > +** frint32z s0, s0 > +** ret > +*/ > +float > +f1 (float x) > +{ > + int y = x; > + return (float) y; > +} > + > +/* > +** f2: > +** frint64z s0, s0 > +** ret > +*/ > +float > +f2 (float x) > +{ > + long long int y = x; > + return (float) y; > +} > + > +/* > +** f3: > +** frint32z d0, d0 > +** ret > +*/ > +double > +f3 (double x) > +{ > + int y = x; > + return (double) y; > +} > + > +/* > +** f4: > +** frint64z d0, d0 > +** ret > +*/ > +double > +f4 (double x) > +{ > + long long int y = x; > + return (double) y; > +} > + > +float > +f1_dont (float x) > +{ > + unsigned int y = x; > + return (float) y; > +} > + > +float > +f2_dont (float x) > +{ > + unsigned long long int y = x; > + return (float) y; > +} > + > +double > +f3_dont (double x) > +{ > + unsigned int y = x; > + return (double) y; > +} > + > +double > +f4_dont (double x) > +{ > + unsigned long long int y = x; > + return (double) y; > +} > + > +double > +f5_dont (double x) > +{ > + signed short y = x; > + return (double) y; > +} > + > +/* Make sure the 'dont's don't generate any frintNz. */ > +/* { dg-final { scan-assembler-times {frint32z} 2 } } */ > +/* { dg-final { scan-assembler-times {frint64z} 2 } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/frintnz_vec.c > b/gcc/testsuite/gcc.target/aarch64/frintnz_vec.c > new file mode 100644 > index > 0000000000000000000000000000000000000000..801d65ea8325cb680691286aab42747f43b90687 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/frintnz_vec.c > @@ -0,0 +1,47 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O3 -march=armv8.5-a" } */ > +/* { dg-require-effective-target aarch64_frintnzx_ok } */ Same here. > +/* { dg-final { check-function-bodies "**" "" } } */ > + > +#define TEST(name,float_type,int_type) > \ > +void \ > +name (float_type * __restrict__ x, float_type * __restrict__ y, int n) \ > +{ \ > + for (int i = 0; i < n; ++i) \ > + { \ > + int_type x_i = x[i]; \ > + y[i] = (float_type) x_i; \ > + } \ > +} > + > +/* > +** f1: > +** ... > +** frint32z v[0-9]+\.4s, v[0-9]+\.4s > +** ... > +*/ > +TEST(f1, float, int) > + > +/* > +** f2: > +** ... > +** frint64z v[0-9]+\.4s, v[0-9]+\.4s > +** ... > +*/ > +TEST(f2, float, long long) > + > +/* > +** f3: > +** ... > +** frint32z v[0-9]+\.2d, v[0-9]+\.2d > +** ... > +*/ > +TEST(f3, double, int) > + > +/* > +** f4: > +** ... > +** frint64z v[0-9]+\.2d, v[0-9]+\.2d > +** ... > +*/ > +TEST(f4, double, long long) > […] > diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c > index > f2625a2ff4089739326ce11785f1b68678c07f0e..435f2f4f5aeb2ed4c503c7b6a97d375634ae4514 > 100644 > --- a/gcc/tree-vect-stmts.c > +++ b/gcc/tree-vect-stmts.c > @@ -1625,7 +1625,8 @@ vect_finish_stmt_generation (vec_info *vinfo, > > static internal_fn > vectorizable_internal_function (combined_fn cfn, tree fndecl, > - tree vectype_out, tree vectype_in) > + tree vectype_out, tree vectype_in, > + tree *vectypes) Should be described in the comment above the function. > { > internal_fn ifn; > if (internal_fn_p (cfn)) > @@ -1637,8 +1638,12 @@ vectorizable_internal_function (combined_fn cfn, tree > fndecl, > const direct_internal_fn_info &info = direct_internal_fn (ifn); > if (info.vectorizable) > { > - tree type0 = (info.type0 < 0 ? vectype_out : vectype_in); > - tree type1 = (info.type1 < 0 ? vectype_out : vectype_in); > + tree type0 = (info.type0 < 0 ? vectype_out : vectypes[info.type0]); > + if (!type0) > + type0 = vectype_in; > + tree type1 = (info.type1 < 0 ? vectype_out : vectypes[info.type1]); > + if (!type1) > + type1 = vectype_in; > if (direct_internal_fn_supported_p (ifn, tree_pair (type0, type1), > OPTIMIZE_FOR_SPEED)) > return ifn; > @@ -3263,18 +3268,40 @@ vectorizable_call (vec_info *vinfo, > rhs_type = unsigned_type_node; > } > > - int mask_opno = -1; > + /* The argument that is not of the same type as the others. */ > + int diff_opno = -1; > + bool masked = false; > if (internal_fn_p (cfn)) > - mask_opno = internal_fn_mask_index (as_internal_fn (cfn)); > + { > + if (cfn == CFN_FTRUNC_INT) > + /* For FTRUNC this represents the argument that carries the type of the > + intermediate signed integer. */ > + diff_opno = 1; > + else > + { > + /* For masked operations this represents the argument that carries the > + mask. */ > + diff_opno = internal_fn_mask_index (as_internal_fn (cfn)); > + masked = diff_opno >= 0; Nit: excess space after “>=”. > + } > + } I think it would be better to add a new flag to direct_internal_fn_info to say whether type1 is scalar, rather than check based on function code. type1 would then provide the value of diff_opno above. Also, I think diff_opno should be separate from mask_opno. Maybe scalar_opno would be a better name. This would probably be simpler if we used: internal_fn ifn = associated_internal_fn (cfn, lhs_type); before the loop (with lhs_type being new), then used ifn to get the direct_internal_fn_info and passed ifn to vectorizable_internal_function. > for (i = 0; i < nargs; i++) > { > - if ((int) i == mask_opno) > + if ((int) i == diff_opno) > { > - if (!vect_check_scalar_mask (vinfo, stmt_info, slp_node, mask_opno, > - &op, &slp_op[i], &dt[i], &vectypes[i])) > - return false; > - continue; > + if (masked) > + { > + if (!vect_check_scalar_mask (vinfo, stmt_info, slp_node, > + diff_opno, &op, &slp_op[i], &dt[i], > + &vectypes[i])) > + return false; > + } > + else > + { > + vectypes[i] = TREE_TYPE (gimple_call_arg (stmt, i)); > + continue; > + } > } > > if (!vect_is_simple_use (vinfo, stmt_info, slp_node, > @@ -3286,27 +3313,30 @@ vectorizable_call (vec_info *vinfo, > return false; > } > > - /* We can only handle calls with arguments of the same type. */ > - if (rhs_type > - && !types_compatible_p (rhs_type, TREE_TYPE (op))) > + if ((int) i != diff_opno) Is this ever false? It looks the continue above handles the other case. > { > - if (dump_enabled_p ()) > - dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > - "argument types differ.\n"); > - return false; > - } > - if (!rhs_type) > - rhs_type = TREE_TYPE (op); > + /* We can only handle calls with arguments of the same type. */ > + if (rhs_type > + && !types_compatible_p (rhs_type, TREE_TYPE (op))) > + { > + if (dump_enabled_p ()) > + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > + "argument types differ.\n"); > + return false; > + } > + if (!rhs_type) > + rhs_type = TREE_TYPE (op); > > - if (!vectype_in) > - vectype_in = vectypes[i]; > - else if (vectypes[i] > - && !types_compatible_p (vectypes[i], vectype_in)) > - { > - if (dump_enabled_p ()) > - dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > - "argument vector types differ.\n"); > - return false; > + if (!vectype_in) > + vectype_in = vectypes[i]; > + else if (vectypes[i] > + && !types_compatible_p (vectypes[i], vectype_in)) > + { > + if (dump_enabled_p ()) > + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > + "argument vector types differ.\n"); > + return false; > + } > } > } > /* If all arguments are external or constant defs, infer the vector type > @@ -3382,8 +3412,8 @@ vectorizable_call (vec_info *vinfo, > || (modifier == NARROW > && simple_integer_narrowing (vectype_out, vectype_in, > &convert_code)))) > - ifn = vectorizable_internal_function (cfn, callee, vectype_out, > - vectype_in); > + ifn = vectorizable_internal_function (cfn, callee, vectype_out, > vectype_in, > + &vectypes[0]); > > /* If that fails, try asking for a target-specific built-in function. */ > if (ifn == IFN_LAST) > @@ -3461,7 +3491,7 @@ vectorizable_call (vec_info *vinfo, > > if (loop_vinfo > && LOOP_VINFO_CAN_USE_PARTIAL_VECTORS_P (loop_vinfo) > - && (reduc_idx >= 0 || mask_opno >= 0)) > + && (reduc_idx >= 0 || masked)) > { > if (reduc_idx >= 0 > && (cond_fn == IFN_LAST > @@ -3481,8 +3511,8 @@ vectorizable_call (vec_info *vinfo, > ? SLP_TREE_NUMBER_OF_VEC_STMTS (slp_node) > : ncopies); > tree scalar_mask = NULL_TREE; > - if (mask_opno >= 0) > - scalar_mask = gimple_call_arg (stmt_info->stmt, mask_opno); > + if (masked) > + scalar_mask = gimple_call_arg (stmt_info->stmt, diff_opno); > vect_record_loop_mask (loop_vinfo, masks, nvectors, > vectype_out, scalar_mask); > } > @@ -3547,7 +3577,7 @@ vectorizable_call (vec_info *vinfo, > { > /* We don't define any narrowing conditional functions > at present. */ > - gcc_assert (mask_opno < 0); > + gcc_assert (!masked); > tree half_res = make_ssa_name (vectype_in); > gcall *call > = gimple_build_call_internal_vec (ifn, vargs); > @@ -3567,16 +3597,16 @@ vectorizable_call (vec_info *vinfo, > } > else > { > - if (mask_opno >= 0 && masked_loop_p) > + if (masked && masked_loop_p) > { > unsigned int vec_num = vec_oprnds0.length (); > /* Always true for SLP. */ > gcc_assert (ncopies == 1); > tree mask = vect_get_loop_mask (gsi, masks, vec_num, > vectype_out, i); > - vargs[mask_opno] = prepare_vec_mask > + vargs[diff_opno] = prepare_vec_mask > (loop_vinfo, TREE_TYPE (mask), mask, > - vargs[mask_opno], gsi); > + vargs[diff_opno], gsi); > } > > gcall *call; > @@ -3614,13 +3644,13 @@ vectorizable_call (vec_info *vinfo, > if (masked_loop_p && reduc_idx >= 0) > vargs[varg++] = vargs[reduc_idx + 1]; > > - if (mask_opno >= 0 && masked_loop_p) > + if (masked && masked_loop_p) > { > tree mask = vect_get_loop_mask (gsi, masks, ncopies, > vectype_out, j); > - vargs[mask_opno] > + vargs[diff_opno] > = prepare_vec_mask (loop_vinfo, TREE_TYPE (mask), mask, > - vargs[mask_opno], gsi); > + vargs[diff_opno], gsi); > } > > gimple *new_stmt; > @@ -3639,7 +3669,7 @@ vectorizable_call (vec_info *vinfo, > { > /* We don't define any narrowing conditional functions at > present. */ > - gcc_assert (mask_opno < 0); > + gcc_assert (!masked); > tree half_res = make_ssa_name (vectype_in); > gcall *call = gimple_build_call_internal_vec (ifn, vargs); > gimple_call_set_lhs (call, half_res); > @@ -3683,7 +3713,7 @@ vectorizable_call (vec_info *vinfo, > { > auto_vec<vec<tree> > vec_defs (nargs); > /* We don't define any narrowing conditional functions at present. */ > - gcc_assert (mask_opno < 0); > + gcc_assert (!masked); > for (j = 0; j < ncopies; ++j) > { > /* Build argument list for the vectorized call. */ > diff --git a/gcc/tree.h b/gcc/tree.h > index > 318019c4dc5373271551f5d9a48dadb57a29d4a7..770d0ddfcc9a7acda01ed2fafa61eab0f1ba4cfa > 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -6558,4 +6558,12 @@ extern unsigned fndecl_dealloc_argno (tree); > object or pointer. Otherwise return null. */ > extern tree get_attr_nonstring_decl (tree, tree * = NULL); > > +/* Return the type, or for a complex or vector type the type of its > + elements. */ > +extern tree element_type (tree); > + > +/* Return the precision of the type, or for a complex or vector type the > + precision of the type of its elements. */ > +extern unsigned int element_precision (const_tree); > + > #endif /* GCC_TREE_H */ > diff --git a/gcc/tree.c b/gcc/tree.c > index > d98b77db50b29b22dc9af1f98cd86044f62af019..81e66dd710ce6bc237f508655cfb437b40ec0bfa > 100644 > --- a/gcc/tree.c > +++ b/gcc/tree.c > @@ -6646,11 +6646,11 @@ valid_constant_size_p (const_tree size, > cst_size_error *perr /* = NULL */) > return true; > } > > -/* Return the precision of the type, or for a complex or vector type the > - precision of the type of its elements. */ > +/* Return the type, or for a complex or vector type the type of its > + elements. */ > > -unsigned int > -element_precision (const_tree type) > +tree > +element_type (tree type) > { > if (!TYPE_P (type)) > type = TREE_TYPE (type); > @@ -6658,7 +6658,16 @@ element_precision (const_tree type) > if (code == COMPLEX_TYPE || code == VECTOR_TYPE) > type = TREE_TYPE (type); > > - return TYPE_PRECISION (type); > + return const_cast<tree> (type); The const_cast<> is redundant. Sorry for not thinking about it before, but we should probably have a test for the SLP case. E.g.: for (int i = 0; i < n; i += 2) { int_type x_i0 = x[i]; int_type x_i1 = x[i + 1]; y[i] = (float_type) x_i1; y[i + 1] = (float_type) x_i0; } (with a permute thrown in for good measure). This will make sure that the (separate) SLP group matching code handles the call correctly. Thanks, Richard