Rework VEC_PERM_EXPR folding
authorRichard Sandiford <richard.sandiford@linaro.org>
Tue, 2 Jan 2018 18:27:15 +0000 (18:27 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Tue, 2 Jan 2018 18:27:15 +0000 (18:27 +0000)
This patch reworks the VEC_PERM_EXPR folding so that more of it
works for variable-length vectors.  E.g. it means that we can
now recognise variable-length permutes that reduce to a single
vector, or cases in which a variable-length permute only needs
one input.  There should be no functional change for fixed-length
vectors.

2018-01-02  Richard Sandiford  <richard.sandiford@linaro.org>

gcc/
* selftest.h (selftest::vec_perm_indices_c_tests): Declare.
* selftest-run-tests.c (selftest::run_tests): Call it.
* vector-builder.h (vector_builder::operator ==): New function.
(vector_builder::operator !=): Likewise.
* vec-perm-indices.h (vec_perm_indices::series_p): Declare.
(vec_perm_indices::all_from_input_p): New function.
* vec-perm-indices.c (vec_perm_indices::series_p): Likewise.
(test_vec_perm_12, selftest::vec_perm_indices_c_tests): Likewise.
* fold-const.c (fold_ternary_loc): Use tree_to_vec_perm_builder
instead of reading the VECTOR_CST directly.  Detect whether both
vector inputs are the same before constructing the vec_perm_indices,
and update the number of inputs argument accordingly.  Use the
utility functions added above.  Only construct sel2 if we need to.

From-SVN: r256098

gcc/ChangeLog
gcc/fold-const.c
gcc/selftest-run-tests.c
gcc/selftest.h
gcc/vec-perm-indices.c
gcc/vec-perm-indices.h
gcc/vector-builder.h

index c5b62cd3628e1ab6cc152197b549e3d741d71321..9e3face038b38f382527ebfb43d1eed989429ac1 100644 (file)
@@ -1,3 +1,19 @@
+2018-01-02  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * selftest.h (selftest::vec_perm_indices_c_tests): Declare.
+       * selftest-run-tests.c (selftest::run_tests): Call it.
+       * vector-builder.h (vector_builder::operator ==): New function.
+       (vector_builder::operator !=): Likewise.
+       * vec-perm-indices.h (vec_perm_indices::series_p): Declare.
+       (vec_perm_indices::all_from_input_p): New function.
+       * vec-perm-indices.c (vec_perm_indices::series_p): Likewise.
+       (test_vec_perm_12, selftest::vec_perm_indices_c_tests): Likewise.
+       * fold-const.c (fold_ternary_loc): Use tree_to_vec_perm_builder
+       instead of reading the VECTOR_CST directly.  Detect whether both
+       vector inputs are the same before constructing the vec_perm_indices,
+       and update the number of inputs argument accordingly.  Use the
+       utility functions added above.  Only construct sel2 if we need to.
+
 2018-01-02  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * optabs.c (expand_vec_perm_var): Use an explicit encoding for
index ca0d8fd9319fda9e0c4223ad9703aef9f1e207bb..f28970b2a8755e8187d3dace1d0e6c6ccb6b839c 100644 (file)
@@ -11705,99 +11705,65 @@ fold_ternary_loc (location_t loc, enum tree_code code, tree type,
     case VEC_PERM_EXPR:
       if (TREE_CODE (arg2) == VECTOR_CST)
        {
-         unsigned int nelts = VECTOR_CST_NELTS (arg2), i, mask, mask2;
-         bool need_mask_canon = false;
-         bool need_mask_canon2 = false;
-         bool all_in_vec0 = true;
-         bool all_in_vec1 = true;
-         bool maybe_identity = true;
-         bool single_arg = (op0 == op1);
-         bool changed = false;
-
-         mask2 = 2 * nelts - 1;
-         mask = single_arg ? (nelts - 1) : mask2;
-         gcc_assert (nelts == TYPE_VECTOR_SUBPARTS (type));
-         vec_perm_builder sel (nelts, nelts, 1);
-         vec_perm_builder sel2 (nelts, nelts, 1);
-         for (i = 0; i < nelts; i++)
-           {
-             tree val = VECTOR_CST_ELT (arg2, i);
-             if (TREE_CODE (val) != INTEGER_CST)
-               return NULL_TREE;
-
-             /* Make sure that the perm value is in an acceptable
-                range.  */
-             wi::tree_to_wide_ref t = wi::to_wide (val);
-             need_mask_canon |= wi::gtu_p (t, mask);
-             need_mask_canon2 |= wi::gtu_p (t, mask2);
-             unsigned int elt = t.to_uhwi () & mask;
-             unsigned int elt2 = t.to_uhwi () & mask2;
-
-             if (elt < nelts)
-               all_in_vec1 = false;
-             else
-               all_in_vec0 = false;
-
-             if ((elt & (nelts - 1)) != i)
-               maybe_identity = false;
+         /* Build a vector of integers from the tree mask.  */
+         vec_perm_builder builder;
+         if (!tree_to_vec_perm_builder (&builder, arg2))
+           return NULL_TREE;
 
-             sel.quick_push (elt);
-             sel2.quick_push (elt2);
-           }
+         /* Create a vec_perm_indices for the integer vector.  */
+         unsigned int nelts = TYPE_VECTOR_SUBPARTS (type);
+         bool single_arg = (op0 == op1);
+         vec_perm_indices sel (builder, single_arg ? 1 : 2, nelts);
 
-         if (maybe_identity)
-           {
-             if (all_in_vec0)
-               return op0;
-             if (all_in_vec1)
-               return op1;
-           }
+         /* Check for cases that fold to OP0 or OP1 in their original
+            element order.  */
+         if (sel.series_p (0, 1, 0, 1))
+           return op0;
+         if (sel.series_p (0, 1, nelts, 1))
+           return op1;
 
-         if (all_in_vec0)
-           op1 = op0;
-         else if (all_in_vec1)
+         if (!single_arg)
            {
-             op0 = op1;
-             for (i = 0; i < nelts; i++)
-               sel[i] -= nelts;
-             need_mask_canon = true;
+             if (sel.all_from_input_p (0))
+               op1 = op0;
+             else if (sel.all_from_input_p (1))
+               {
+                 op0 = op1;
+                 sel.rotate_inputs (1);
+               }
            }
 
-         vec_perm_indices indices (sel, 2, nelts);
          if ((TREE_CODE (op0) == VECTOR_CST
               || TREE_CODE (op0) == CONSTRUCTOR)
              && (TREE_CODE (op1) == VECTOR_CST
                  || TREE_CODE (op1) == CONSTRUCTOR))
            {
-             tree t = fold_vec_perm (type, op0, op1, indices);
+             tree t = fold_vec_perm (type, op0, op1, sel);
              if (t != NULL_TREE)
                return t;
            }
 
-         if (op0 == op1 && !single_arg)
-           changed = true;
-
-         /* Some targets are deficient and fail to expand a single
-            argument permutation while still allowing an equivalent
-            2-argument version.  */
-         if (need_mask_canon && arg2 == op2
-             && !can_vec_perm_const_p (TYPE_MODE (type), indices, false)
-             && can_vec_perm_const_p (TYPE_MODE (type),
-                                      vec_perm_indices (sel2, 2, nelts),
-                                      false))
-           {
-             need_mask_canon = need_mask_canon2;
-             sel.truncate (0);
-             sel.splice (sel2);
-           }
+         bool changed = (op0 == op1 && !single_arg);
 
-         if (need_mask_canon && arg2 == op2)
+         /* Generate a canonical form of the selector.  */
+         if (arg2 == op2 && sel.encoding () != builder)
            {
-             tree eltype = TREE_TYPE (TREE_TYPE (arg2));
-             tree_vector_builder tsel (TREE_TYPE (arg2), nelts, 1);
-             for (i = 0; i < nelts; i++)
-               tsel.quick_push (build_int_cst (eltype, sel[i]));
-             op2 = tsel.build ();
+             /* Some targets are deficient and fail to expand a single
+                argument permutation while still allowing an equivalent
+                2-argument version.  */
+             if (sel.ninputs () == 2
+                 || can_vec_perm_const_p (TYPE_MODE (type), sel, false))
+               op2 = vec_perm_indices_to_tree (TREE_TYPE (arg2), sel);
+             else
+               {
+                 vec_perm_indices sel2 (builder, 2, nelts);
+                 if (can_vec_perm_const_p (TYPE_MODE (type), sel2, false))
+                   op2 = vec_perm_indices_to_tree (TREE_TYPE (arg2), sel2);
+                 else
+                   /* Not directly supported with either encoding,
+                      so use the preferred form.  */
+                   op2 = vec_perm_indices_to_tree (TREE_TYPE (arg2), sel);
+               }
              changed = true;
            }
 
index 63d6e071ecac5a2a7922aba16a16fdd04be559d9..6af0f02ccbbe574091d4d67691a10c2fde19d56c 100644 (file)
@@ -73,6 +73,7 @@ selftest::run_tests ()
 
   /* Mid-level data structures.  */
   input_c_tests ();
+  vec_perm_indices_c_tests ();
   tree_c_tests ();
   gimple_c_tests ();
   rtl_tests_c_tests ();
index ab8a7c0c71c4ac6b1e78102ff1d584e271ee7219..23ca66557c1a19d90b3fd64053c4df25f20a5992 100644 (file)
@@ -215,6 +215,7 @@ extern void vec_c_tests ();
 extern void wide_int_cc_tests ();
 extern void predict_c_tests ();
 extern void simplify_rtx_c_tests ();
+extern void vec_perm_indices_c_tests ();
 
 extern int num_passes;
 
index 558ab2efcf2bf30cec5737747e4cae154eb5a920..3eb9c41cad0e4648c65e93f5e052cdd02d497e24 100644 (file)
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "memmodel.h"
 #include "emit-rtl.h"
+#include "selftest.h"
 
 /* Switch to a new permutation vector that selects between NINPUTS vector
    inputs that have NELTS_PER_INPUT elements each.  Take the elements of the
@@ -85,6 +86,54 @@ vec_perm_indices::rotate_inputs (int delta)
     m_encoding[i] = clamp (m_encoding[i] + element_delta);
 }
 
+/* Return true if index OUT_BASE + I * OUT_STEP selects input
+   element IN_BASE + I * IN_STEP.  */
+
+bool
+vec_perm_indices::series_p (unsigned int out_base, unsigned int out_step,
+                           element_type in_base, element_type in_step) const
+{
+  /* Check the base value.  */
+  if (clamp (m_encoding.elt (out_base)) != clamp (in_base))
+    return false;
+
+  unsigned int full_nelts = m_encoding.full_nelts ();
+  unsigned int npatterns = m_encoding.npatterns ();
+
+  /* Calculate which multiple of OUT_STEP elements we need to get
+     back to the same pattern.  */
+  unsigned int cycle_length = least_common_multiple (out_step, npatterns);
+
+  /* Check the steps.  */
+  in_step = clamp (in_step);
+  out_base += out_step;
+  unsigned int limit = 0;
+  for (;;)
+    {
+      /* Succeed if we've checked all the elements in the vector.  */
+      if (out_base >= full_nelts)
+       return true;
+
+      if (out_base >= npatterns)
+       {
+         /* We've got to the end of the "foreground" values.  Check
+            2 elements from each pattern in the "background" values.  */
+         if (limit == 0)
+           limit = out_base + cycle_length * 2;
+         else if (out_base >= limit)
+           return true;
+       }
+
+      element_type v0 = m_encoding.elt (out_base - out_step);
+      element_type v1 = m_encoding.elt (out_base);
+      if (clamp (v1 - v0) != in_step)
+       return false;
+
+      out_base += out_step;
+    }
+  return true;
+}
+
 /* Return true if all elements of the permutation vector are in the range
    [START, START + SIZE).  */
 
@@ -180,3 +229,52 @@ vec_perm_indices_to_rtx (machine_mode mode, const vec_perm_indices &indices)
     RTVEC_ELT (v, i) = gen_int_mode (indices[i], GET_MODE_INNER (mode));
   return gen_rtx_CONST_VECTOR (mode, v);
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Test a 12-element vector.  */
+
+static void
+test_vec_perm_12 (void)
+{
+  vec_perm_builder builder (12, 12, 1);
+  for (unsigned int i = 0; i < 4; ++i)
+    {
+      builder.quick_push (i * 5);
+      builder.quick_push (3 + i);
+      builder.quick_push (2 + 3 * i);
+    }
+  vec_perm_indices indices (builder, 1, 12);
+  ASSERT_TRUE (indices.series_p (0, 3, 0, 5));
+  ASSERT_FALSE (indices.series_p (0, 3, 3, 5));
+  ASSERT_FALSE (indices.series_p (0, 3, 0, 8));
+  ASSERT_TRUE (indices.series_p (1, 3, 3, 1));
+  ASSERT_TRUE (indices.series_p (2, 3, 2, 3));
+
+  ASSERT_TRUE (indices.series_p (0, 4, 0, 4));
+  ASSERT_FALSE (indices.series_p (1, 4, 3, 4));
+
+  ASSERT_TRUE (indices.series_p (0, 6, 0, 10));
+  ASSERT_FALSE (indices.series_p (0, 6, 0, 100));
+
+  ASSERT_FALSE (indices.series_p (1, 10, 3, 7));
+  ASSERT_TRUE (indices.series_p (1, 10, 3, 8));
+
+  ASSERT_TRUE (indices.series_p (0, 12, 0, 10));
+  ASSERT_TRUE (indices.series_p (0, 12, 0, 11));
+  ASSERT_TRUE (indices.series_p (0, 12, 0, 100));
+}
+
+/* Run selftests for this file.  */
+
+void
+vec_perm_indices_c_tests ()
+{
+  test_vec_perm_12 ();
+}
+
+} // namespace selftest
+
+#endif
index 1048f558d97c6e07a2f60e1f5515aa383fd76725..52b65a51e4b1830582e8fb36d1c19b038efd8161 100644 (file)
@@ -77,7 +77,9 @@ public:
 
   element_type clamp (element_type) const;
   element_type operator[] (unsigned int i) const;
+  bool series_p (unsigned int, unsigned int, element_type, element_type) const;
   bool all_in_range_p (element_type, element_type) const;
+  bool all_from_input_p (unsigned int) const;
 
 private:
   vec_perm_indices (const vec_perm_indices &);
@@ -134,4 +136,13 @@ vec_perm_indices::operator[] (unsigned int i) const
   return clamp (m_encoding.elt (i));
 }
 
+/* Return true if the permutation vector only selects elements from
+   input I.  */
+
+inline bool
+vec_perm_indices::all_from_input_p (unsigned int i) const
+{
+  return all_in_range_p (i * m_nelts_per_input, m_nelts_per_input);
+}
+
 #endif
index 0edd6f5c0927ba091c240ffb631ffc112c54974c..74e0b7655adc1c50b2c33fd8e9dc98692bd10718 100644 (file)
@@ -97,6 +97,9 @@ public:
   bool encoded_full_vector_p () const;
   T elt (unsigned int) const;
 
+  bool operator == (const Derived &) const;
+  bool operator != (const Derived &x) const { return !operator == (x); }
+
   void finalize ();
 
 protected:
@@ -168,6 +171,26 @@ vector_builder<T, Derived>::new_vector (unsigned int full_nelts,
   this->truncate (0);
 }
 
+/* Return true if this vector and OTHER have the same elements and
+   are encoded in the same way.  */
+
+template<typename T, typename Derived>
+bool
+vector_builder<T, Derived>::operator == (const Derived &other) const
+{
+  if (m_full_nelts != other.m_full_nelts
+      || m_npatterns != other.m_npatterns
+      || m_nelts_per_pattern != other.m_nelts_per_pattern)
+    return false;
+
+  unsigned int nelts = encoded_nelts ();
+  for (unsigned int i = 0; i < nelts; ++i)
+    if (!derived ()->equal_p ((*this)[i], other[i]))
+      return false;
+
+  return true;
+}
+
 /* Return the value of vector element I, which might or might not be
    encoded explicitly.  */