On Tue, 8 Aug 2023 at 15:27, Richard Sandiford
<richard.sandif...@arm.com> wrote:
>
> Prathamesh Kulkarni <prathamesh.kulka...@linaro.org> writes:
> > On Fri, 4 Aug 2023 at 20:36, Richard Sandiford
> > <richard.sandif...@arm.com> wrote:
> >>
> >> Full review this time, sorry for the skipping the tests earlier.
> > Thanks for the detailed review! Please find my responses inline below.
> >>
> >> Prathamesh Kulkarni <prathamesh.kulka...@linaro.org> writes:
> >> > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> >> > index 7e5494dfd39..680d0e54fd4 100644
> >> > --- a/gcc/fold-const.cc
> >> > +++ b/gcc/fold-const.cc
> >> > @@ -85,6 +85,10 @@ along with GCC; see the file COPYING3.  If not see
> >> >  #include "vec-perm-indices.h"
> >> >  #include "asan.h"
> >> >  #include "gimple-range.h"
> >> > +#include <algorithm>
> >>
> >> This should be included by defining INCLUDE_ALGORITHM instead.
> > Done. Just curious, why do we use this macro instead of directly
> > including <algorithm> ?
>
> AIUI, one of the reasons for having every file start with includes
> of config.h and (b)system.h, in that order, is to ensure that a small
> and predictable amount of GCC-specific stuff happens before including
> the system header files.  That helps to avoid OS-specific clashes between
> GCC code and system headers.
>
> But another major reason is that system.h ends by poisoning a lot of
> stuff that system headers would be entitled to use.
Ah OK, thanks for the clarification!
>
> >> > +  tree_vector_builder builder (vectype, npatterns, nelts_per_pattern);
> >> > +
> >> > +  // Fill a0 for each pattern
> >> > +  for (unsigned i = 0; i < npatterns; i++)
> >> > +    builder.quick_push (build_int_cst (inner_type, rand () % 100));
> >> > +
> >> > +  if (nelts_per_pattern == 1)
> >> > +    return builder.build ();
> >> > +
> >> > +  // Fill a1 for each pattern
> >> > +  for (unsigned i = 0; i < npatterns; i++)
> >> > +    builder.quick_push (build_int_cst (inner_type, rand () % 100));
> >> > +
> >> > +  if (nelts_per_pattern == 2)
> >> > +    return builder.build ();
> >> > +
> >> > +  for (unsigned i = npatterns * 2; i < npatterns * nelts_per_pattern; 
> >> > i++)
> >> > +    {
> >> > +      tree prev_elem = builder[i - npatterns];
> >> > +      int prev_elem_val = TREE_INT_CST_LOW (prev_elem);
> >> > +      int val = prev_elem_val + S;
> >> > +      builder.quick_push (build_int_cst (inner_type, val));
> >> > +    }
> >> > +
> >> > +  return builder.build ();
> >> > +}
> >> > +
> >> > +static void
> >> > +validate_res (unsigned npatterns, unsigned nelts_per_pattern,
> >> > +           tree res, tree *expected_res)
> >> > +{
> >> > +  ASSERT_TRUE (VECTOR_CST_NPATTERNS (res) == npatterns);
> >> > +  ASSERT_TRUE (VECTOR_CST_NELTS_PER_PATTERN (res) == nelts_per_pattern);
> >>
> >> I don't think this is safe when the inputs are randomised.  E.g. we
> >> could by chance end up with a vector of all zeros, which would have
> >> a single pattern and a single element per pattern, regardless of the
> >> shapes of the inputs.
> >>
> >> Given the way that vector_builder<T, Shape, Derived>::finalize
> >> canonicalises the encoding, it should be safe to use:
> >>
> >> * VECTOR_CST_NPATTERNS (res) <= npatterns
> >> * vector_cst_encoded_nelts (res) <= npatterns * nelts_per_pattern
> >>
> >> If we do that then...
> >>
> >> > +
> >> > +  for (unsigned i = 0; i < vector_cst_encoded_nelts (res); i++)
> >>
> >> ...this loop bound should be npatterns * nelts_per_pattern instead.
> > Ah indeed. Fixed, thanks.
>
> The patch instead does:
>
>   ASSERT_TRUE (VECTOR_CST_NPATTERNS (res) <= npatterns);
>   ASSERT_TRUE (VECTOR_CST_NELTS_PER_PATTERN (res) <= nelts_per_pattern);
>
> I think the version I suggested is safer.  It's not the goal of the
> canonicalisation algorithm to reduce both npattners and nelts_per_pattern
> individually.  The algorithm can increase nelts_per_pattern in order
> to decrease npatterns.
Oops, sorry I misread, will fix in the next patch.
>
> >> > +  {
> >> > +    tree arg0 = build_vec_cst_rand (integer_type_node, 1, 3, 2);
> >> > +    tree arg1 = build_vec_cst_rand (integer_type_node, 1, 3, 2);
> >> > +    poly_uint64 arg0_len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> >> > +
> >> > +    vec_perm_builder builder (arg0_len, 1, 3);
> >> > +    builder.quick_push (arg0_len);
> >> > +    builder.quick_push (arg0_len + 1);
> >> > +    builder.quick_push (arg0_len + 2);
> >> > +
> >> > +    vec_perm_indices sel (builder, 2, arg0_len);
> >> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel, 
> >> > NULL, true);
> >> > +    tree expected_res[] = { vector_cst_elt (arg1, 0), vector_cst_elt 
> >> > (arg1, 1),
> >> > +                         vector_cst_elt (arg1, 2) };
> >> > +    validate_res (1, 3, res, expected_res);
> >> > +  }
> >> > +
> >> > +  /* Case 3: Leading element of arg1, stepped sequence: pattern 0 of 
> >> > arg0.
> >> > +     sel = {len, 0, 0, 0, 2, 0, ...}
> >> > +     npatterns = 2, nelts_per_pattern = 3.
> >> > +     Use extra pattern {0, ...} to lower number of elements per 
> >> > pattern.  */
> >> > +  {
> >> > +    tree arg0 = build_vec_cst_rand (char_type_node, 1, 3, 2);
> >> > +    tree arg1 = build_vec_cst_rand (char_type_node, 1, 3, 2);
> >> > +    poly_uint64 arg0_len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> >> > +
> >> > +    vec_perm_builder builder (arg0_len, 2, 3);
> >> > +    builder.quick_push (arg0_len);
> >> > +    int mask_elems[] = { 0, 0, 0, 2, 0 };
> >> > +    for (int i = 0; i < 5; i++)
> >> > +      builder.quick_push (mask_elems[i]);
> >>
> >> This leaves one of the elements unspecified.
> > Sorry, I didn't understand.
> > It first pushes len in:
> > builder.quick_push (arg0_len)
> > and then pushes the remaining indices in the loop:
> > for (int i = 0; i < 5; i++)
> >   builder.quick_push (mask_elems[i])
> > So overall, builder will have 6 elements: {len, 0, 0, 0, 2, 0}
>
> Ah, right.  But in that case I think it would be clearer to put arg0_len
> in mask_elems.
Right of course, sorry. For some reason I thought earlier I couldn't
initialize array of poly_uint64 :/
>
> > +/* Try to fold permutation of ARG0 and ARG1 with SEL selector when
> > +   the input vectors are VECTOR_CST. Return NULL_TREE otherwise.
> > +   REASON has same purpose as described in
> > +   valid_mask_for_fold_vec_perm_cst_p.  */
> > +
> > +
>
> Nit: too much vertical space.
Will fix in next patch, thanks.
>
> > +/* Invoke tests for fold_vec_perm_cst.  */
> > +
> > +static void
> > +test ()
> > +{
> > +  /* Conditionally execute fold_vec_perm_cst tests, if target supports
> > +     VLA vectors. Use a compile time check so we avoid instantiating
> > +     poly_uint64 with N > 1 on targets that do not support VLA vectors.  */
> > +  if constexpr (poly_int_traits<poly_uint64>::num_coeffs > 1)
>
> That's a C++17 feature, so we can't use it.
>
> Instead, how about:
>
> static bool
> is_simple_vla_size (poly_uint64 size)
> {
>   if (size.is_constant ())
>     return false;
>   for (int i = 1; i < ARRAY_SIZE (size.coeffs); ++i)
>     if (size[i] != (i <= 1 ? size[0] : 0))
Just wondering is this should be (i == 1 ? size[0] : 0) since i is
initialized to 1 ?
IIUC, is_simple_vla_size should return true for polynomials of first
degree and having same coeff like 4 + 4x ?
>       return false;
>   return true;
> }
>
>
>   FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_INT)
>     {
>       auto nunits = GET_MODE_NUNITS (mode);
>       if (!is_simple_vla_size (nunits))
>         continue;
>       if (nunits[0] ...)
>         test_... (mode);
>       ...
>
>     }
>
> test_vnx4si_v4si and test_v4si_vnx4si look good.  But with the
> loop structure above, I think we can apply the test_vnx4si and
> test_vnx16qi to more cases.  So the classification isn't the
> exact number of elements, but instead a limit.
>
> I think the nunits[0] conditions for test_vnx4si are as follows
> (inspection only, so could be wrong):
>
> > +/* Test cases where result and input vectors are VNx4SI  */
> > +
> > +static void
> > +test_vnx4si (machine_mode vmode)
> > +{
> > +  /* Case 1: mask = {0, ...} */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 1, 1);
> > +    builder.quick_push (0);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (res, 0) };
This should be { vector_cst_elt (arg0, 0) }; will fix in next patch.
> > +    validate_res (1, 1, res, expected_res);
> > +  }
>
> nunits[0] >= 2 (could be all nunits if the inputs had nelts_per_pattern==1,
> which I think would be better)
IIUC, the vectors that can be used for a particular test should have
nunits[0] >= res_npatterns,
where res_npatterns is as computed in fold_vec_perm_cst without the
canonicalization ?
For above test -- res_npatterns = max(2, max (2, 1)) == 2, so we
require nunits[0] >= 2 ?
Which implies we can use above test for vectors with length 2 + 2x, 4 + 4x, etc.

Sorry if this sounds like a silly question -- Won't nunits[0] >= 2
cover all nunits,
since a vector, at a minimum, will contain 2 elements ?

I will send a patch shortly addressing above suggestions.

Thanks,
Prathamesh
>
> > +
> > +  /* Case 2: mask = {len, ...} */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 1, 1);
> > +    builder.quick_push (len);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (arg1, 0) };
> > +    validate_res (1, 1, res, expected_res);
> > +  }
>
> same
>
> > +
> > +  /* Case 3: mask = { 0, len, ... } */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 2, 1);
> > +    builder.quick_push (0);
> > +    builder.quick_push (len);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (arg0, 0), vector_cst_elt 
> > (arg1, 0) };
> > +    validate_res (2, 1, res, expected_res);
> > +  }
>
> nunits[0] >= 2
>
> > +
> > +  /* Case 4: mask = { 0, len, 1, len+1, ... } */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 2, 2);
> > +    builder.quick_push (0);
> > +    builder.quick_push (len);
> > +    builder.quick_push (1);
> > +    builder.quick_push (len + 1);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (arg0, 0), vector_cst_elt 
> > (arg1, 0),
> > +                         vector_cst_elt (arg0, 1), vector_cst_elt (arg1, 1)
> > +                       };
> > +    validate_res (2, 2, res, expected_res);
> > +  }
>
> nunits[0] >= 2
>
> > +
> > +  /* Case 5: mask = { 0, len, 1, len+1, .... }
> > +     npatterns = 4, nelts_per_pattern = 1 */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 4, 1);
> > +    builder.quick_push (0);
> > +    builder.quick_push (len);
> > +    builder.quick_push (1);
> > +    builder.quick_push (len + 1);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (arg0, 0), vector_cst_elt 
> > (arg1, 0),
> > +                         vector_cst_elt (arg0, 1), vector_cst_elt (arg1, 1)
> > +                       };
> > +    validate_res (4, 1, res, expected_res);
> > +  }
>
> nunits[0] >= 4
>
> > +
> > +  /* Case 6: mask = {0, 4, ...}
> > +     npatterns = 1, nelts_per_pattern = 2.
> > +     This should return NULL_TREE because the index 4 may choose
> > +     from either arg0 or arg1 depending on vector length.  */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (len, 1, 2);
> > +    builder.quick_push (0);
> > +    builder.quick_push (4);
> > +    vec_perm_indices sel (builder, 2, len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +    ASSERT_TRUE (res == NULL_TREE);
> > +  }
>
> nunits[0] == 2 or == 4 (could be <= 4 if the inputs had nelts_per_pattern==1)
>
> > +
> > +  /* Case 7: npatterns(arg0) = 4 > npatterns(sel) = 2
> > +     mask = {0, len, 1, len + 1, ...}
> > +     sel_npatterns = 2, sel_nelts_per_pattern = 2.  */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    tree arg1 = build_vec_cst_rand (vmode, 2, 3, 1);
> > +    poly_uint64 arg0_len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (arg0_len, 2, 2);
> > +    builder.quick_push (0);
> > +    builder.quick_push (arg0_len);
> > +    builder.quick_push (1);
> > +    builder.quick_push (arg0_len + 1);
> > +    vec_perm_indices sel (builder, 2, arg0_len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +
> > +    tree expected_res[] = { vector_cst_elt (arg0, 0), vector_cst_elt 
> > (arg1, 0),
> > +                         vector_cst_elt (arg0, 1), vector_cst_elt (arg1, 1)
> > +                       };
> > +    validate_res (2, 2, res, expected_res);
> > +  }
>
> nunits[0] >= 2
>
>
> > +
> > +  /* Case 8: sel = {0, 1, 2, ...}
> > +     npatterns = 1, nelts_per_pattern = 3
> > +     expected res: { arg0[0], arg0[1], arg0[2], ... } */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 1, 3, 2);
> > +    tree arg1 = build_vec_cst_rand (vmode, 1, 3, 2);
> > +    poly_uint64 arg0_len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (arg0_len, 1, 3);
> > +    builder.quick_push (0);
> > +    builder.quick_push (1);
> > +    builder.quick_push (2);
> > +
> > +    vec_perm_indices sel (builder, 2, arg0_len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel);
> > +    tree expected_res[] = { vector_cst_elt (arg0, 0), vector_cst_elt 
> > (arg0, 1),
> > +                         vector_cst_elt (arg0, 2) };
> > +    validate_res (1, 3, res, expected_res);
> > +  }
>
> all nunits[0]
>
> > +
> > +  /* Case 9: sel = {len, len + 1, len + 2, ... }
> > +     npatterns = 1, nelts_per_pattern = 3
> > +     expected res: { op1[0], op1[1], op1[2], ... }  */
> > +  {
> > +    tree arg0 = build_vec_cst_rand (vmode, 1, 3, 2);
> > +    tree arg1 = build_vec_cst_rand (vmode, 1, 3, 2);
> > +    poly_uint64 arg0_len = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
> > +
> > +    vec_perm_builder builder (arg0_len, 1, 3);
> > +    builder.quick_push (arg0_len);
> > +    builder.quick_push (arg0_len + 1);
> > +    builder.quick_push (arg0_len + 2);
> > +
> > +    vec_perm_indices sel (builder, 2, arg0_len);
> > +    tree res = fold_vec_perm_cst (TREE_TYPE (arg0), arg0, arg1, sel, NULL);
> > +    tree expected_res[] = { vector_cst_elt (arg1, 0), vector_cst_elt 
> > (arg1, 1),
> > +                         vector_cst_elt (arg1, 2) };
> > +    validate_res (1, 3, res, expected_res);
> > +  }
>
> all nunits[0]
>
> Same idea for the others.
>
> Thanks,
> Richard

Reply via email to