https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108692
--- Comment #2 from Jakub Jelinek <jakub at gcc dot gnu.org> --- So, on simplified -O2 -ftree-vectorize testcase with trunk: int foo (signed char *x, signed char *y, int n) { int i, r = 0; signed char a, b; for (i = 0; i < n; i++) { a = x[i]; b = y[i]; int c = (unsigned char) a - (unsigned char) b; r = r + (c < 0 ? -c : c); } return r; } everything looks ok to me until vect_recog_sad_pattern is called. The interesting part of the loop in question is: <bb 3> [local count: 955630225]: # i_22 = PHI <i_20(6), 0(5)> # r_23 = PHI <r_19(6), 0(5)> ... a.0_5 = (unsigned char) a_15; _6 = (int) a.0_5; b.1_7 = (unsigned char) b_17; _8 = (int) b.1_7; c_18 = _6 - _8; _9 = ABS_EXPR <c_18>; r_19 = _9 + r_23; with 15/17 having signed char type, 5/7 unsigned char and everything else int. Now, when vect_recog_sad_pattern is called, it sees as diff_stmt_vinfo->stmt patt_34 = (a.0_5) w- (b.1_7); which is reasonable, abs_stmt_vinfo was patt_30 = ABS_EXPR <patt_33>; where 30 is signed short, patt_33 too set to (signed short) patt_34 and 34 unsigned short. Still, the widening subtraction is done with zero extensions from unsigned char operands to unsigned short. But vect_recog_sad_pattern calls 1325 if (!vect_widened_op_tree (vinfo, diff_stmt_vinfo, MINUS_EXPR, WIDEN_MINUS_EXPR, 1326 false, 2, unprom, &half_type)) 1327 return NULL; and instead of returning a.0_5 and b.1_7 as the unpromoted operands and unsigned char as half_type, it returns a_15 and b_17 as the unpromoted operands and signed char as half_type. I'd think if in vect_widened_op_tree after the early checks rhs_code != code we shouldn't look through further promotions and just accept what we have. as