z13 supports only non-signaling vector comparisons.  This means we
cannot vectorize LT, LE, GT, GE and LTGT when compiling for z13.
However, we cannot express this restriction today: the code only checks
whether vcond$a$b optab exists, which does not contain information about
the operation.

Introduce a function that checks whether back-end supports vector
comparisons with individual rtx codes by matching vcond expander's third
argument with a fake comparison with the corresponding rtx code.

gcc/ChangeLog:

2019-08-21  Ilya Leoshkevich  <i...@linux.ibm.com>

        * Makefile.in (GTFILES): Add optabs.c.
        * optabs-tree.c (expand_vec_cond_expr_p): Use
        can_vector_compare_p.
        * optabs.c (binop_key): Binary operation cache key.
        (binop_hasher): Binary operation cache hasher.
        (cached_binops): Binary operation cache.
        (get_cached_binop): New function that returns a cached binary
        operation or creates a new one.
        (can_vector_compare_p): New function.
        * optabs.h (enum can_vector_compare_purpose): New enum. Not
        really needed today, but can be used to extend the support to
        e.g. vec_cmp if need arises.
        (can_vector_compare_p): New function.
---
 gcc/Makefile.in   |  2 +-
 gcc/optabs-tree.c | 11 +++++--
 gcc/optabs.c      | 79 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/optabs.h      | 15 +++++++++
 4 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 597dc01328b..d2207da5657 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2541,7 +2541,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h 
$(srcdir)/coretypes.h \
   $(srcdir)/function.c $(srcdir)/except.c \
   $(srcdir)/ggc-tests.c \
   $(srcdir)/gcse.c $(srcdir)/godump.c \
-  $(srcdir)/lists.c $(srcdir)/optabs-libfuncs.c \
+  $(srcdir)/lists.c $(srcdir)/optabs.c $(srcdir)/optabs-libfuncs.c \
   $(srcdir)/profile.c $(srcdir)/mcf.c \
   $(srcdir)/reg-stack.c $(srcdir)/cfgrtl.c \
   $(srcdir)/stor-layout.c \
diff --git a/gcc/optabs-tree.c b/gcc/optabs-tree.c
index 8157798cc71..e68bb39c021 100644
--- a/gcc/optabs-tree.c
+++ b/gcc/optabs-tree.c
@@ -23,7 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "insn-codes.h"
+#include "rtl.h"
 #include "tree.h"
+#include "memmodel.h"
+#include "optabs.h"
 #include "optabs-tree.h"
 #include "stor-layout.h"
 
@@ -347,8 +350,12 @@ expand_vec_cond_expr_p (tree value_type, tree cmp_op_type, 
enum tree_code code)
       || maybe_ne (GET_MODE_NUNITS (value_mode), GET_MODE_NUNITS 
(cmp_op_mode)))
     return false;
 
-  if (get_vcond_icode (TYPE_MODE (value_type), TYPE_MODE (cmp_op_type),
-                      TYPE_UNSIGNED (cmp_op_type)) == CODE_FOR_nothing
+  bool unsigned_p = TYPE_UNSIGNED (cmp_op_type);
+  if (((get_vcond_icode (TYPE_MODE (value_type), TYPE_MODE (cmp_op_type),
+                        unsigned_p) == CODE_FOR_nothing)
+       || !can_vector_compare_p (get_rtx_code (code, unsigned_p),
+                                TYPE_MODE (value_type),
+                                TYPE_MODE (cmp_op_type), cvcp_vcond))
       && ((code != EQ_EXPR && code != NE_EXPR)
          || get_vcond_eq_icode (TYPE_MODE (value_type),
                                 TYPE_MODE (cmp_op_type)) == CODE_FOR_nothing))
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 9e54dda6e7f..07b4d824822 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "recog.h"
 #include "diagnostic-core.h"
 #include "rtx-vector-builder.h"
+#include "hash-table.h"
 
 /* Include insn-config.h before expr.h so that HAVE_conditional_move
    is properly defined.  */
@@ -3819,6 +3820,82 @@ can_compare_p (enum rtx_code code, machine_mode mode,
   return 0;
 }
 
+/* can_vector_compare_p presents fake rtx binary operations to the the back-end
+   in order to determine its capabilities.  In order to avoid creating fake
+   operations on each call, values from previous calls are cached in a global
+   cached_binops hash_table.  It contains rtxes, which can be looked up using
+   binop_keys.  */
+
+struct binop_key {
+  enum rtx_code code;        /* Operation code.  */
+  machine_mode value_mode;   /* Result mode.     */
+  machine_mode cmp_op_mode;  /* Operand mode.    */
+};
+
+struct binop_hasher : pointer_hash_mark<rtx>, ggc_cache_remove<rtx> {
+  typedef rtx value_type;
+  typedef binop_key compare_type;
+
+  static hashval_t
+  hash (enum rtx_code code, machine_mode value_mode, machine_mode cmp_op_mode)
+  {
+    inchash::hash hstate (0);
+    hstate.add_int (code);
+    hstate.add_int (value_mode);
+    hstate.add_int (cmp_op_mode);
+    return hstate.end ();
+  }
+
+  static hashval_t
+  hash (const rtx &ref)
+  {
+    return hash (GET_CODE (ref), GET_MODE (ref), GET_MODE (XEXP (ref, 0)));
+  }
+
+  static bool
+  equal (const rtx &ref1, const binop_key &ref2)
+  {
+    return (GET_CODE (ref1) == ref2.code)
+          && (GET_MODE (ref1) == ref2.value_mode)
+          && (GET_MODE (XEXP (ref1, 0)) == ref2.cmp_op_mode);
+  }
+};
+
+static GTY ((cache)) hash_table<binop_hasher> *cached_binops;
+
+static rtx
+get_cached_binop (enum rtx_code code, machine_mode value_mode,
+                 machine_mode cmp_op_mode)
+{
+  if (!cached_binops)
+    cached_binops = hash_table<binop_hasher>::create_ggc (1024);
+  binop_key key = { code, value_mode, cmp_op_mode };
+  hashval_t hash = binop_hasher::hash (code, value_mode, cmp_op_mode);
+  rtx *slot = cached_binops->find_slot_with_hash (key, hash, INSERT);
+  if (!*slot)
+    *slot = gen_rtx_fmt_ee (code, value_mode, gen_reg_rtx (cmp_op_mode),
+                           gen_reg_rtx (cmp_op_mode));
+  return *slot;
+}
+
+bool
+can_vector_compare_p (enum rtx_code code, machine_mode value_mode,
+                     machine_mode cmp_op_mode,
+                     enum can_vector_compare_purpose purpose)
+{
+  enum insn_code icode;
+  bool unsigned_p = (code == LTU || code == LEU || code == GTU || code == GEU);
+  rtx test = get_cached_binop(code, value_mode, cmp_op_mode);
+
+  if (purpose == cvcp_vcond
+      && (icode = get_vcond_icode (value_mode, cmp_op_mode, unsigned_p))
+        != CODE_FOR_nothing
+      && insn_operand_matches (icode, 3, test))
+    return true;
+
+  return false;
+}
+
 /* This function is called when we are going to emit a compare instruction that
    compares the values found in X and Y, using the rtl operator COMPARISON.
 
@@ -7481,3 +7558,5 @@ expand_jump_insn (enum insn_code icode, unsigned int nops,
   if (!maybe_expand_jump_insn (icode, nops, ops))
     gcc_unreachable ();
 }
+
+#include "gt-optabs.h"
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 897bb5d4443..2b2338a67af 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -242,6 +242,21 @@ enum can_compare_purpose
    (without splitting it into pieces).  */
 extern int can_compare_p (enum rtx_code, machine_mode,
                          enum can_compare_purpose);
+
+/* The various uses that a vector comparison can have; used by
+   can_vector_compare_p.  So far only vcond is defined, vec_cmp is a possible
+   future extension.  */
+enum can_vector_compare_purpose
+{
+  cvcp_vcond
+};
+
+/* Return whether back-end can emit a vector comparison insn(s) using a given
+   CODE, with operands with CMP_OP_MODE, producing a result with VALUE_MODE,
+   in order to achieve a PURPOSE.  */
+extern bool can_vector_compare_p (enum rtx_code, machine_mode, machine_mode,
+                                 enum can_vector_compare_purpose);
+
 extern rtx prepare_operand (enum insn_code, rtx, int, machine_mode,
                            machine_mode, int);
 /* Emit a pair of rtl insns to compare two rtx's and to jump
-- 
2.21.0

Reply via email to