Hi!

The documentation for this function says that it wants to go through all
(integral) conversions (both promotions and demotions), as long as the
whole sequence of conversions is functionally equivalent to (optionally)
casting the returned value to a signed or unsigned type with the same
precision as the returned value (depending on the ->type filled in)
and then (optionally) promoting it to the original argument's type.

On the following testcase, we have:
  _39 = (long int) iftmp.0_19;
in the loop and
  _4 = _2 >> a.4_3;
  iftmp.0_19 = (signed char) _4;
before the loop, with
  int _4;
  signed char iftmp.0_19;
  long int _39;
Currently we return _4, which is incorrect, we can't represent the series of
conversions as simple zero or sign extension from int to long int, because
there is a (signed char) cast in the middle.  So, instead of returning _4 we
need to return iftmp.0_19 in this case.  That doesn't mean we need to stop
the search for further candidates at that point, but just that all further
candidates need to be at most with that precision.

The following patch fixes that, bootstrapped/regtested on x86_64-linux and
i686-linux, ok for trunk?

2018-11-16  Jakub Jelinek  <ja...@redhat.com>

        PR tree-optimization/87546
        * tree-vect-patterns.c (vect_look_through_possible_promotion): Add
        min_precision variable, initially set it to orig_precision, only does
        something if op_type's precision is <= min_precision and update
        min_precision whenever calling set_op.

        * gcc.dg/vect/O3-pr87546.c: New test.

--- gcc/tree-vect-patterns.c.jj 2018-10-23 10:13:25.200876565 +0200
+++ gcc/tree-vect-patterns.c    2018-11-16 16:53:08.668610905 +0100
@@ -367,6 +367,7 @@ vect_look_through_possible_promotion (ve
   tree res = NULL_TREE;
   tree op_type = TREE_TYPE (op);
   unsigned int orig_precision = TYPE_PRECISION (op_type);
+  unsigned int min_precision = orig_precision;
   stmt_vec_info caster = NULL;
   while (TREE_CODE (op) == SSA_NAME && INTEGRAL_TYPE_P (op_type))
     {
@@ -385,7 +386,7 @@ vect_look_through_possible_promotion (ve
         This copes with cases such as the result of an arithmetic
         operation being truncated before being stored, and where that
         arithmetic operation has been recognized as an over-widened one.  */
-      if (TYPE_PRECISION (op_type) <= orig_precision)
+      if (TYPE_PRECISION (op_type) <= min_precision)
        {
          /* Use OP as the UNPROM described above if we haven't yet
             found a promotion, or if using the new input preserves the
@@ -393,7 +394,10 @@ vect_look_through_possible_promotion (ve
          if (!res
              || TYPE_PRECISION (unprom->type) == orig_precision
              || TYPE_SIGN (unprom->type) == TYPE_SIGN (op_type))
-           unprom->set_op (op, dt, caster);
+           {
+             unprom->set_op (op, dt, caster);
+             min_precision = TYPE_PRECISION (op_type);
+           }
          /* Stop if we've already seen a promotion and if this
             conversion does more than change the sign.  */
          else if (TYPE_PRECISION (op_type)
--- gcc/testsuite/gcc.dg/vect/O3-pr87546.c.jj   2018-11-16 17:03:39.294149070 
+0100
+++ gcc/testsuite/gcc.dg/vect/O3-pr87546.c      2018-11-16 17:05:25.620385151 
+0100
@@ -0,0 +1,29 @@
+#include "tree-vect.h"
+
+int a;
+long b, f;
+signed char c, g;
+short int d = 219;
+int e[64];
+
+__attribute__((noipa)) void
+foo (void)
+{
+  asm volatile ("" : : "g" (&a), "g" (&d) : "memory");
+  for (c = 0; c < 64; c++)
+    {
+      g = d < 0 ? d : d >> a;
+      f = g + b;
+      e[c] = f;
+    }
+  if (e[1] != (signed char) d)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  check_vect ();
+  foo ();
+  return 0;
+}


        Jakub

Reply via email to