]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/optabs-query.c
* doc/extend.texi (Common Function Attributes): Clarify
[thirdparty/gcc.git] / gcc / optabs-query.c
index 204ca60249f528348941c83717c90eeb450a4831..04c8d08115b3383a1906861e8f5595de28b5d840 100644 (file)
@@ -1,5 +1,5 @@
 /* IR-agnostic target query functions relating to optabs
-   Copyright (C) 1987-2017 Free Software Foundation, Inc.
+   Copyright (C) 1987-2019 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "insn-config.h"
 #include "rtl.h"
 #include "recog.h"
+#include "vec-perm-indices.h"
 
 struct target_optabs default_target_optabs;
 struct target_optabs *this_fn_optabs = &default_target_optabs;
@@ -212,7 +213,7 @@ get_best_extraction_insn (extraction_insn *insn,
          FOR_EACH_MODE_FROM (mode_iter, mode)
            {
              mode = mode_iter.require ();
-             if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (field_mode)
+             if (maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (field_mode))
                  || TRULY_NOOP_TRUNCATION_MODES_P (insn->field_mode,
                                                    field_mode))
                break;
@@ -345,100 +346,152 @@ can_conditionally_move_p (machine_mode mode)
   return direct_optab_handler (movcc_optab, mode) != CODE_FOR_nothing;
 }
 
-/* Return true if VEC_PERM_EXPR of arbitrary input vectors can be
-   expanded using SIMD extensions of the CPU.  SEL may be NULL, which
-   stands for an unknown constant.  Note that additional permutations
-   representing whole-vector shifts may also be handled via the vec_shr
-   optab, but only where the second input vector is entirely constant
-   zeroes; this case is not dealt with here.  */
+/* If a target doesn't implement a permute on a vector with multibyte
+   elements, we can try to do the same permute on byte elements.
+   If this makes sense for vector mode MODE then return the appropriate
+   byte vector mode.  */
 
-bool
-can_vec_perm_p (machine_mode mode, bool variable, vec_perm_indices *sel)
+opt_machine_mode
+qimode_for_vec_perm (machine_mode mode)
 {
   machine_mode qimode;
+  if (GET_MODE_INNER (mode) != QImode
+      && mode_for_vector (QImode, GET_MODE_SIZE (mode)).exists (&qimode)
+      && VECTOR_MODE_P (qimode))
+    return qimode;
+  return opt_machine_mode ();
+}
+
+/* Return true if selector SEL can be represented in the integer
+   equivalent of vector mode MODE.  */
 
+bool
+selector_fits_mode_p (machine_mode mode, const vec_perm_indices &sel)
+{
+  unsigned HOST_WIDE_INT mask = GET_MODE_MASK (GET_MODE_INNER (mode));
+  return (mask == HOST_WIDE_INT_M1U
+         || sel.all_in_range_p (0, mask + 1));
+}
+
+/* Return true if VEC_PERM_EXPRs with variable selector operands can be
+   expanded using SIMD extensions of the CPU.  MODE is the mode of the
+   vectors being permuted.  */
+
+bool
+can_vec_perm_var_p (machine_mode mode)
+{
   /* If the target doesn't implement a vector mode for the vector type,
      then no operations are supported.  */
   if (!VECTOR_MODE_P (mode))
     return false;
 
-  if (!variable)
-    {
-      if (direct_optab_handler (vec_perm_const_optab, mode) != CODE_FOR_nothing
-         && (sel == NULL
-             || targetm.vectorize.vec_perm_const_ok == NULL
-             || targetm.vectorize.vec_perm_const_ok (mode, *sel)))
-       return true;
-    }
-
   if (direct_optab_handler (vec_perm_optab, mode) != CODE_FOR_nothing)
     return true;
 
   /* We allow fallback to a QI vector mode, and adjust the mask.  */
-  if (GET_MODE_INNER (mode) == QImode
-      || !mode_for_vector (QImode, GET_MODE_SIZE (mode)).exists (&qimode)
-      || !VECTOR_MODE_P (qimode))
+  machine_mode qimode;
+  if (!qimode_for_vec_perm (mode).exists (&qimode)
+      || maybe_gt (GET_MODE_NUNITS (qimode), GET_MODE_MASK (QImode) + 1))
     return false;
 
-  /* ??? For completeness, we ought to check the QImode version of
-      vec_perm_const_optab.  But all users of this implicit lowering
-      feature implement the variable vec_perm_optab.  */
   if (direct_optab_handler (vec_perm_optab, qimode) == CODE_FOR_nothing)
     return false;
 
   /* In order to support the lowering of variable permutations,
      we need to support shifts and adds.  */
-  if (variable)
-    {
-      if (GET_MODE_UNIT_SIZE (mode) > 2
-         && optab_handler (ashl_optab, mode) == CODE_FOR_nothing
-         && optab_handler (vashl_optab, mode) == CODE_FOR_nothing)
-       return false;
-      if (optab_handler (add_optab, qimode) == CODE_FOR_nothing)
-       return false;
-    }
+  if (GET_MODE_UNIT_SIZE (mode) > 2
+      && optab_handler (ashl_optab, mode) == CODE_FOR_nothing
+      && optab_handler (vashl_optab, mode) == CODE_FOR_nothing)
+    return false;
+  if (optab_handler (add_optab, qimode) == CODE_FOR_nothing)
+    return false;
 
   return true;
 }
 
-/* Like optab_handler, but for widening_operations that have a
-   TO_MODE and a FROM_MODE.  */
+/* Return true if the target directly supports VEC_PERM_EXPRs on vectors
+   of mode MODE using the selector SEL.  ALLOW_VARIABLE_P is true if it
+   is acceptable to force the selector into a register and use a variable
+   permute (if the target supports that).
 
-enum insn_code
-widening_optab_handler (optab op, machine_mode to_mode,
-                       machine_mode from_mode)
+   Note that additional permutations representing whole-vector shifts may
+   also be handled via the vec_shr optab, but only where the second input
+   vector is entirely constant zeroes; this case is not dealt with here.  */
+
+bool
+can_vec_perm_const_p (machine_mode mode, const vec_perm_indices &sel,
+                     bool allow_variable_p)
 {
-  unsigned scode = (op << 16) | to_mode;
-  if (to_mode != from_mode && from_mode != VOIDmode)
+  /* If the target doesn't implement a vector mode for the vector type,
+     then no operations are supported.  */
+  if (!VECTOR_MODE_P (mode))
+    return false;
+
+  /* It's probably cheaper to test for the variable case first.  */
+  if (allow_variable_p && selector_fits_mode_p (mode, sel))
     {
-      /* ??? Why does find_widening_optab_handler_and_mode attempt to
-        widen things that can't be widened?  E.g. add_optab... */
-      if (op > LAST_CONV_OPTAB)
-       return CODE_FOR_nothing;
-      scode |= from_mode << 8;
+      if (direct_optab_handler (vec_perm_optab, mode) != CODE_FOR_nothing)
+       return true;
+
+      /* Unlike can_vec_perm_var_p, we don't need to test for optabs
+        related computing the QImode selector, since that happens at
+        compile time.  */
+      machine_mode qimode;
+      if (qimode_for_vec_perm (mode).exists (&qimode))
+       {
+         vec_perm_indices qimode_indices;
+         qimode_indices.new_expanded_vector (sel, GET_MODE_UNIT_SIZE (mode));
+         if (selector_fits_mode_p (qimode, qimode_indices)
+             && (direct_optab_handler (vec_perm_optab, qimode)
+                 != CODE_FOR_nothing))
+           return true;
+       }
     }
-  return raw_optab_handler (scode);
+
+  if (targetm.vectorize.vec_perm_const != NULL)
+    {
+      if (targetm.vectorize.vec_perm_const (mode, NULL_RTX, NULL_RTX,
+                                           NULL_RTX, sel))
+       return true;
+
+      /* ??? For completeness, we ought to check the QImode version of
+        vec_perm_const_optab.  But all users of this implicit lowering
+        feature implement the variable vec_perm_optab, and the ia64
+        port specifically doesn't want us to lower V2SF operations
+        into integer operations.  */
+    }
+
+  return false;
 }
 
 /* Find a widening optab even if it doesn't widen as much as we want.
    E.g. if from_mode is HImode, and to_mode is DImode, and there is no
-   direct HI->SI insn, then return SI->DI, if that exists.
-   If PERMIT_NON_WIDENING is non-zero then this can be used with
-   non-widening optabs also.  */
+   direct HI->SI insn, then return SI->DI, if that exists.  */
 
 enum insn_code
 find_widening_optab_handler_and_mode (optab op, machine_mode to_mode,
                                      machine_mode from_mode,
-                                     int permit_non_widening,
                                      machine_mode *found_mode)
 {
-  for (; (permit_non_widening || from_mode != to_mode)
-        && GET_MODE_SIZE (from_mode) <= GET_MODE_SIZE (to_mode)
-        && from_mode != VOIDmode;
-       from_mode = GET_MODE_WIDER_MODE (from_mode).else_void ())
+  machine_mode limit_mode = to_mode;
+  if (is_a <scalar_int_mode> (from_mode))
+    {
+      gcc_checking_assert (is_a <scalar_int_mode> (to_mode)
+                          && known_lt (GET_MODE_PRECISION (from_mode),
+                                       GET_MODE_PRECISION (to_mode)));
+      /* The modes after FROM_MODE are all MODE_INT, so the only
+        MODE_PARTIAL_INT mode we consider is FROM_MODE itself.
+        If LIMIT_MODE is MODE_PARTIAL_INT, stop at the containing
+        MODE_INT.  */
+      if (GET_MODE_CLASS (limit_mode) == MODE_PARTIAL_INT)
+       limit_mode = GET_MODE_WIDER_MODE (limit_mode).require ();
+    }
+  else
+    gcc_checking_assert (GET_MODE_CLASS (from_mode) == GET_MODE_CLASS (to_mode)
+                        && from_mode < to_mode);
+  FOR_EACH_MODE (from_mode, from_mode, limit_mode)
     {
-      enum insn_code handler = widening_optab_handler (op, to_mode,
-                                                      from_mode);
+      enum insn_code handler = convert_optab_handler (op, to_mode, from_mode);
 
       if (handler != CODE_FOR_nothing)
        {
@@ -459,7 +512,6 @@ int
 can_mult_highpart_p (machine_mode mode, bool uns_p)
 {
   optab op;
-  unsigned i, nunits;
 
   op = uns_p ? umul_highpart_optab : smul_highpart_optab;
   if (optab_handler (op, mode) != CODE_FOR_nothing)
@@ -469,7 +521,7 @@ can_mult_highpart_p (machine_mode mode, bool uns_p)
   if (GET_MODE_CLASS (mode) != MODE_VECTOR_INT)
     return 0;
 
-  nunits = GET_MODE_NUNITS (mode);
+  poly_int64 nunits = GET_MODE_NUNITS (mode);
 
   op = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab;
   if (optab_handler (op, mode) != CODE_FOR_nothing)
@@ -477,12 +529,14 @@ can_mult_highpart_p (machine_mode mode, bool uns_p)
       op = uns_p ? vec_widen_umult_odd_optab : vec_widen_smult_odd_optab;
       if (optab_handler (op, mode) != CODE_FOR_nothing)
        {
-         auto_vec_perm_indices sel (nunits);
-         for (i = 0; i < nunits; ++i)
+         /* The encoding has 2 interleaved stepped patterns.  */
+         vec_perm_builder sel (nunits, 2, 3);
+         for (unsigned int i = 0; i < 6; ++i)
            sel.quick_push (!BYTES_BIG_ENDIAN
                            + (i & ~1)
                            + ((i & 1) ? nunits : 0));
-         if (can_vec_perm_p (mode, false, &sel))
+         vec_perm_indices indices (sel, 2, nunits);
+         if (can_vec_perm_const_p (mode, indices))
            return 2;
        }
     }
@@ -493,10 +547,12 @@ can_mult_highpart_p (machine_mode mode, bool uns_p)
       op = uns_p ? vec_widen_umult_lo_optab : vec_widen_smult_lo_optab;
       if (optab_handler (op, mode) != CODE_FOR_nothing)
        {
-         auto_vec_perm_indices sel (nunits);
-         for (i = 0; i < nunits; ++i)
+         /* The encoding has a single stepped pattern.  */
+         vec_perm_builder sel (nunits, 1, 3);
+         for (unsigned int i = 0; i < 3; ++i)
            sel.quick_push (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
-         if (can_vec_perm_p (mode, false, &sel))
+         vec_perm_indices indices (sel, 2, nunits);
+         if (can_vec_perm_const_p (mode, indices))
            return 3;
        }
     }
@@ -513,7 +569,6 @@ can_vec_mask_load_store_p (machine_mode mode,
 {
   optab op = is_load ? maskload_optab : maskstore_optab;
   machine_mode vmode;
-  unsigned int vector_sizes;
 
   /* If mode is vector mode, check it directly.  */
   if (VECTOR_MODE_P (mode))
@@ -537,14 +592,14 @@ can_vec_mask_load_store_p (machine_mode mode,
       && convert_optab_handler (op, vmode, mask_mode) != CODE_FOR_nothing)
     return true;
 
-  vector_sizes = targetm.vectorize.autovectorize_vector_sizes ();
-  while (vector_sizes != 0)
+  auto_vector_sizes vector_sizes;
+  targetm.vectorize.autovectorize_vector_sizes (&vector_sizes, true);
+  for (unsigned int i = 0; i < vector_sizes.length (); ++i)
     {
-      unsigned int cur = 1 << floor_log2 (vector_sizes);
-      vector_sizes &= ~cur;
-      if (cur <= GET_MODE_SIZE (smode))
+      poly_uint64 cur = vector_sizes[i];
+      poly_uint64 nunits;
+      if (!multiple_p (cur, GET_MODE_SIZE (smode), &nunits))
        continue;
-      unsigned int nunits = cur / GET_MODE_SIZE (smode);
       if (mode_for_vector (smode, nunits).exists (&vmode)
          && VECTOR_MODE_P (vmode)
          && targetm.vectorize.get_mask_mode (nunits, cur).exists (&mask_mode)
@@ -612,7 +667,7 @@ can_atomic_load_p (machine_mode mode)
   /* If the size of the object is greater than word size on this target,
      then we assume that a load will not be atomic.  Also see
      expand_atomic_load.  */
-  return GET_MODE_PRECISION (mode) <= BITS_PER_WORD;
+  return known_le (GET_MODE_PRECISION (mode), BITS_PER_WORD);
 }
 
 /* Determine whether "1 << x" is relatively cheap in word_mode.  */
@@ -641,3 +696,50 @@ lshift_cheap_p (bool speed_p)
 
   return cheap[speed_p];
 }
+
+/* Return true if optab OP supports at least one mode.  */
+
+static bool
+supports_at_least_one_mode_p (optab op)
+{
+  for (int i = 0; i < NUM_MACHINE_MODES; ++i)
+    if (direct_optab_handler (op, (machine_mode) i) != CODE_FOR_nothing)
+      return true;
+
+  return false;
+}
+
+/* Return true if vec_gather_load is available for at least one vector
+   mode.  */
+
+bool
+supports_vec_gather_load_p ()
+{
+  if (this_fn_optabs->supports_vec_gather_load_cached)
+    return this_fn_optabs->supports_vec_gather_load;
+
+  this_fn_optabs->supports_vec_gather_load_cached = true;
+
+  this_fn_optabs->supports_vec_gather_load
+    = supports_at_least_one_mode_p (gather_load_optab);
+
+  return this_fn_optabs->supports_vec_gather_load;
+}
+
+/* Return true if vec_scatter_store is available for at least one vector
+   mode.  */
+
+bool
+supports_vec_scatter_store_p ()
+{
+  if (this_fn_optabs->supports_vec_scatter_store_cached)
+    return this_fn_optabs->supports_vec_scatter_store;
+
+  this_fn_optabs->supports_vec_scatter_store_cached = true;
+
+  this_fn_optabs->supports_vec_scatter_store
+    = supports_at_least_one_mode_p (scatter_store_optab);
+
+  return this_fn_optabs->supports_vec_scatter_store;
+}
+