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