--- /dev/null
+/* { dg-do compile } */
+
+#define N 128
+
+short x[N];
+short y[N];
+
+void
+f1 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] >>= (y[i] & 15);
+}
+
+void
+f2 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] >>= ((y[i] & 7) + 8);
+}
+
+void
+f3 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] >>= ((y[i] & 7) ^ 11);
+}
+
+void
+f4 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] >>= (y[i] < 15 ? y[i] : 15);
+}
+
+void
+f5 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] >>= (y[i] < 15 ? y[i] : 1);
+}
+
+void
+f6 (void)
+{
+ for (int i = 0; i < N; ++i)
+ x[i] = 32768 >> (y[i] & 15);
+}
+
+/* { dg-final { scan-tree-dump {:11:[^\n]+can narrow to signed:16 without loss [^\n]+>>} "vect" } } */
+/* { dg-final { scan-tree-dump {:18:[^\n]+can narrow to signed:16 without loss [^\n]+>>} "vect" } } */
+/* { dg-final { scan-tree-dump {:25:[^\n]+can narrow to signed:16 without loss [^\n]+>>} "vect" } } */
+/* { dg-final { scan-tree-dump {:32:[^\n]+can narrow to signed:16 without loss [^\n]+>>} "vect" } } */
+/* { dg-final { scan-tree-dump {:39:[^\n]+can narrow to signed:16 without loss [^\n]+>>} "vect" } } */
+/* { dg-final { scan-tree-dump {can narrow to unsigned:16 without loss [^\n]+>>} "vect" } } */
{
switch (code)
{
+ case NEGATE_EXPR:
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
+ case BIT_NOT_EXPR:
case BIT_AND_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
unsigned int nops = gimple_num_ops (stmt);
if (!vect_truncatable_operation_p (code))
- /* Check that all relevant input operands are compatible, and update
- [MIN_VALUE, MAX_VALUE] to include their ranges. */
- for (unsigned int i = 1; i < nops; ++i)
- {
- tree op = gimple_op (stmt, i);
- if (TREE_CODE (op) == INTEGER_CST)
- {
- /* Don't require the integer to have RHS_TYPE (which it might
- not for things like shift amounts, etc.), but do require it
- to fit the type. */
- if (!int_fits_type_p (op, type))
- return;
-
- min_value = wi::min (min_value, wi::to_wide (op, precision), sign);
- max_value = wi::max (max_value, wi::to_wide (op, precision), sign);
- }
- else if (TREE_CODE (op) == SSA_NAME)
- {
- /* Ignore codes that don't take uniform arguments. */
- if (!types_compatible_p (TREE_TYPE (op), type))
- return;
+ {
+ /* Handle operations that can be computed in type T if all inputs
+ and outputs can be represented in type T. Also handle left and
+ right shifts, where (in addition) the maximum shift amount must
+ be less than the number of bits in T. */
+ bool is_shift;
+ switch (code)
+ {
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ is_shift = true;
+ break;
- wide_int op_min_value, op_max_value;
- if (!vect_get_range_info (op, &op_min_value, &op_max_value))
- return;
+ case ABS_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ /* Modulus is excluded because it is typically calculated by doing
+ a division, for which minimum signed / -1 isn't representable in
+ the original signed type. We could take the division range into
+ account instead, if handling modulus ever becomes important. */
+ is_shift = false;
+ break;
- min_value = wi::min (min_value, op_min_value, sign);
- max_value = wi::max (max_value, op_max_value, sign);
- }
- else
+ default:
return;
- }
+ }
+ for (unsigned int i = 1; i < nops; ++i)
+ {
+ tree op = gimple_op (stmt, i);
+ wide_int op_min_value, op_max_value;
+ if (TREE_CODE (op) == INTEGER_CST)
+ {
+ unsigned int op_precision = TYPE_PRECISION (TREE_TYPE (op));
+ op_min_value = op_max_value = wi::to_wide (op, op_precision);
+ }
+ else if (TREE_CODE (op) == SSA_NAME)
+ {
+ if (!vect_get_range_info (op, &op_min_value, &op_max_value))
+ return;
+ }
+ else
+ return;
+
+ if (is_shift && i == 2)
+ {
+ /* There needs to be one more bit than the maximum shift amount.
+
+ If the maximum shift amount is already 1 less than PRECISION
+ then we can't narrow the shift further. Dealing with that
+ case first ensures that we can safely use an unsigned range
+ below.
+
+ op_min_value isn't relevant, since shifts by negative amounts
+ are UB. */
+ if (wi::geu_p (op_max_value, precision - 1))
+ return;
+ unsigned int min_bits = op_max_value.to_uhwi () + 1;
+
+ /* As explained below, we can convert a signed shift into an
+ unsigned shift if the sign bit is always clear. At this
+ point we've already processed the ranges of the output and
+ the first input. */
+ auto op_sign = sign;
+ if (sign == SIGNED && !wi::neg_p (min_value))
+ op_sign = UNSIGNED;
+ op_min_value = wide_int::from (wi::min_value (min_bits, op_sign),
+ precision, op_sign);
+ op_max_value = wide_int::from (wi::max_value (min_bits, op_sign),
+ precision, op_sign);
+ }
+ min_value = wi::min (min_value, op_min_value, sign);
+ max_value = wi::max (max_value, op_max_value, sign);
+ }
+ }
/* Try to switch signed types for unsigned types if we can.
This is better for two reasons. First, unsigned ops tend