@@ -7531,6 +7531,92 @@ fold_real_zero_addition_p (const_tree type, const_tree arg,
return negate || (arg && !tree_expr_maybe_real_minus_zero_p (arg));
}
+/* Subroutine of match.pd that determines if it is safe to optimize
+ a floating point comparison of an integer value, known to be between
+ LO and HI, using comparison operator CMP, against the real constant
+ R in floating point type FMT, as the same integer comparison against
+ the integer constant I, with sign ISIGN.
+
+ For example, with IEEE-754, (float)x == 2.0f may replaced with x == 2
+ because the floating point representations of the neighboring integers
+ (float)1 and (float)3 are distinct from 2.0f, having values 1.0f and
+ 3.0f respectively. On the other hand (float)x == 16777220.0f can't
+ be replaced by x == 16777220 as (float)16777221 is also 1677220.0f
+ due to truncation/rounding.
+*/
+bool
+fold_cmp_float_cst_p (wide_int lo, wide_int hi, enum tree_code cmp,
+ const REAL_VALUE_TYPE *r, format_helper fmt,
+ wide_int i, signop isign)
+{
+ REAL_VALUE_TYPE raw;
+ REAL_VALUE_TYPE rnd;
+ bool check_im1 = true;
+ bool check_ip1 = true;
+
+ switch (cmp)
+ {
+ case EQ_EXPR:
+ case NE_EXPR:
+ /* Check both i-1 and i+1. */
+ break;
+
+ case LT_EXPR:
+ case GE_EXPR:
+ /* Only check i-1. */
+ check_ip1 = false;
+ break;
+
+ case LE_EXPR:
+ case GT_EXPR:
+ /* Only check i+1. */
+ check_im1 = false;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (flag_rounding_math && i != 0)
+ {
+ real_from_integer (&raw, VOIDmode, i, isign);
+ if (!real_identical (r, &raw))
+ return false;
+ }
+
+ if (check_im1 && wi::gt_p (i, lo, isign))
+ {
+ if (flag_rounding_math)
+ {
+ real_from_integer (&raw, VOIDmode, i - 1, isign);
+ real_convert (&rnd, fmt, &raw);
+ if (!real_identical (&raw, &rnd))
+ return false;
+ }
+ else
+ real_from_integer (&rnd, fmt, i - 1, isign);
+ if (real_identical (r, &rnd))
+ return false;
+ }
+
+ if (check_ip1 && wi::lt_p (i, hi, isign))
+ {
+ if (flag_rounding_math)
+ {
+ real_from_integer (&raw, VOIDmode, i + 1, isign);
+ real_convert (&rnd, fmt, &raw);
+ if (!real_identical (&raw, &rnd))
+ return false;
+ }
+ else
+ real_from_integer (&rnd, fmt, i + 1, isign);
+ if (real_identical (r, &rnd))
+ return false;
+ }
+
+ return true;
+}
+
/* Subroutine of match.pd that optimizes comparisons of a division by
a nonzero integer constant against an integer constant, i.e.
X/C1 op C2.
@@ -175,6 +175,12 @@ extern bool integer_valued_real_p (tree, int = 0);
extern bool fold_real_zero_addition_p (const_tree, const_tree, const_tree,
int);
+extern bool fold_cmp_float_cst_p (wide_int lo, wide_int hi,
+ enum tree_code cmp,
+ const REAL_VALUE_TYPE *r,
+ format_helper fmt,
+ wide_int i, signop isign);
+
extern tree combine_comparisons (location_t, enum tree_code, enum tree_code,
enum tree_code, tree, tree, tree);
extern void debug_fold_checksum (const_tree);
@@ -6860,6 +6860,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(with
{
tree itype = TREE_TYPE (@0);
+ signop isign = TYPE_SIGN (itype);
format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@1))));
const REAL_VALUE_TYPE *cst = TREE_REAL_CST_PTR (@1);
/* Be careful to preserve any potential exceptions due to NaNs.
@@ -6869,16 +6870,41 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
&& ((cmp != EQ_EXPR && cmp != NE_EXPR)
|| (cst->signalling
&& HONOR_SNANS (TREE_TYPE (@1))));
+ /* Conversion may raise FPE_INEXACT with -ftrapping-math. */
+ wide_int hi,lo;
+ bool exact_p;
+#if GIMPLE
+ int_range_max vr0;
+ get_range_query (cfun)->range_of_expr (vr0, @0);
+ if (!vr0.undefined_p ())
+ {
+ lo = vr0.lower_bound ();
+ hi = vr0.upper_bound ();
+ int prec = (isign == SIGNED)
+ ? MAX (wi::min_precision (hi, SIGNED),
+ wi::min_precision (lo, SIGNED)) - 1
+ : wi::min_precision (hi, UNSIGNED);
+ wide_int nz = vr0.get_nonzero_bits ();
+ if (nz != 0)
+ prec -= wi::ctz (nz);
+ exact_p = prec <= significand_size (fmt);
+ }
+ else
+#endif
+ {
+ exact_p = fmt.can_represent_integral_type_p (itype);
+ lo = wi::min_value (itype);
+ hi = wi::max_value (itype);
+ }
+ if (flag_trapping_math && !exact_p)
+ exception_p = true;
}
- /* TODO: allow non-fitting itype and SNaNs when
- -fno-trapping-math. */
- (if (fmt.can_represent_integral_type_p (itype) && ! exception_p)
+ (if (! exception_p)
(with
{
- signop isign = TYPE_SIGN (itype);
REAL_VALUE_TYPE imin, imax;
- real_from_integer (&imin, fmt, wi::min_value (itype), isign);
- real_from_integer (&imax, fmt, wi::max_value (itype), isign);
+ real_from_integer (&imin, fmt, lo, isign);
+ real_from_integer (&imax, fmt, hi, isign);
REAL_VALUE_TYPE icst;
if (cmp == GT_EXPR || cmp == GE_EXPR)
@@ -6888,7 +6914,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
else
real_trunc (&icst, fmt, cst);
- bool cst_int_p = !real_isnan (cst) && real_identical (&icst, cst);
+ bool cst_int_p = real_isfinite (cst) && real_identical (&icst, cst);
bool overflow_p = false;
wide_int icst_val
@@ -6902,21 +6928,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (real_compare (GT_EXPR, cst, &imax))
{ constant_boolean_node (cmp == LT_EXPR || cmp == LE_EXPR || cmp == NE_EXPR,
type); })
- /* Remove cast if CST is an integer representable by ITYPE. */
- (if (cst_int_p)
- (cmp @0 { gcc_assert (!overflow_p);
- wide_int_to_tree (itype, icst_val); })
- )
- /* When CST is fractional, optimize
+ /* When CST is fractional, infinity or NaN, then optimize
(FTYPE) N == CST -> 0
(FTYPE) N != CST -> 1. */
- (if (cmp == EQ_EXPR || cmp == NE_EXPR)
+ (if (!cst_int_p
+ && (cmp == EQ_EXPR || cmp == NE_EXPR))
{ constant_boolean_node (cmp == NE_EXPR, type); })
+ /* Remove cast if CST is an integer representable by ITYPE. */
+ (if (cst_int_p
+ && fold_cmp_float_cst_p (lo, hi, cmp, &icst, fmt,
+ icst_val, isign))
+ (cmp @0 { wide_int_to_tree (itype, icst_val); })
+ )
/* Otherwise replace with sensible integer constant. */
- (with
- {
- gcc_checking_assert (!overflow_p);
- }
+ (if (!cst_int_p
+ && fold_cmp_float_cst_p (lo, hi, icmp, &icst, fmt,
+ icst_val, isign))
(icmp @0 { wide_int_to_tree (itype, icst_val); })))))))))
/* Fold A /[ex] B CMP C to A CMP B * C. */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math -frounding-math" } */
+
+int foo1 (int a) {
+ return (float) a != 16777222.0f;
+}
+
+int foo2 (unsigned int b) {
+ return (float) b != 16777222.0f;
+}
+
+int foo3 (unsigned long long c) {
+ return (float) c != 16777222.0f;
+}
+
+int foo4 (long long d) {
+ return (float) d != 16777222.0f;
+}
+
+/* { dg-final { scan-tree-dump-times "\\(float\\)" 4 "optimized" } } */
new file mode 100644
@@ -0,0 +1,200 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math" } */
+
+int foo1 (int p) {
+ return (float) p != 0;
+ /* { dg-final { scan-tree-dump "p_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+int foo2 (unsigned int q) {
+ return (float) q != 0;
+ /* { dg-final { scan-tree-dump "q_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+int foo3 (long long r) {
+ return (float) r != 0;
+ /* { dg-final { scan-tree-dump "r_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+int foo4 (unsigned long long s) {
+ return (float) s != 0;
+ /* { dg-final { scan-tree-dump "s_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+int foo5 (long long t) {
+ return (double) t != 0;
+ /* { dg-final { scan-tree-dump "t_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+int foo6 (unsigned long long u) {
+ return (double) u != 0;
+ /* { dg-final { scan-tree-dump "u_\[0-9]\\(D\\) != 0;" "optimized" } } */
+}
+
+/* Tests when RHS is within range of integer type. */
+
+void in_range (unsigned int x)
+{
+ {
+ volatile int in_range_1;
+ in_range_1 = (float) x > 100.0f;
+ /* { dg-final { scan-tree-dump " > 100;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_2;
+ in_range_2 = (float) x < 200.0f;
+ /* { dg-final { scan-tree-dump " <= 199;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_3;
+ in_range_3 = (float) x > 300.5f;
+ /* { dg-final { scan-tree-dump " > 300;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_4;
+ in_range_4 = (float) x < 400.5f;
+ /* { dg-final { scan-tree-dump " <= 400;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_5;
+ in_range_5 = (float) x == 500.0f;
+ /* { dg-final { scan-tree-dump " == 500;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_6;
+ in_range_6 = (float) x != 600.0f;
+ /* { dg-final { scan-tree-dump " != 600;" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_7;
+ in_range_7 = (float) x == 700.5f;
+ /* { dg-final { scan-tree-dump "in_range_7 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_8;
+ in_range_8 = (float) x != 800.5f;
+ /* { dg-final { scan-tree-dump "in_range_8 ={v} 1" "optimized" } } */
+ }
+}
+
+/* Tests for cases where RHS is out of range of integer type. */
+
+void out_range (unsigned int x)
+{
+ {
+ volatile int out_range_1;
+ out_range_1 = (float) x > -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_1 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_2;
+ out_range_2 = (float) x >= -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_2 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_3;
+ out_range_3 = (float) x < -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_3 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_4;
+ out_range_4 = (float) x <= -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_4 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_5;
+ out_range_5 = (float) x == -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_5 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_6;
+ out_range_6 = (float) x != -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_6 ={v} 1" "optimized" } } */
+ }
+}
+
+/* Tests with non-finite float consts. */
+
+void nonfinite (unsigned int x)
+{
+#define INFINITY __builtin_inff ()
+
+ {
+ volatile int nonfinite_1;
+ nonfinite_1 = (float) x > INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_1 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_2;
+ nonfinite_2 = (float) x >= INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_2 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_3;
+ nonfinite_3 = (float) x < INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_3 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_4;
+ nonfinite_4 = (float) x <= INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_4 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_5;
+ nonfinite_5 = (float) x > -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_5 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_6;
+ nonfinite_6 = (float) x >= -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_6 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_7;
+ nonfinite_7 = (float) x < -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_7 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_8;
+ nonfinite_8 = (float) x <= -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_8 ={v} 0" "optimized" } } */
+ }
+
+#define QNAN __builtin_nanf ("0")
+
+ /* Even for qNaNs, only == and != are quiet. */
+
+ {
+ volatile int nonfinite_9;
+ nonfinite_9 = (float) x == QNAN;
+ /* { dg-final { scan-tree-dump "nonfinite_9 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_10;
+ nonfinite_10 = (float) x != QNAN;
+ /* { dg-final { scan-tree-dump "nonfinite_10 ={v} 1" "optimized" } } */
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "\\(float\\)" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\(double\\)" "optimized" } } */
new file mode 100644
@@ -0,0 +1,218 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo1 (int a) {
+ a &= 4095;
+ return (float) a != 0.0f;
+ /* { dg-final { scan-tree-dump "a_\[0-9] != 0;" "optimized" } } */
+}
+
+int foo2 (unsigned int b) {
+ b &= 4095;
+ return (float) b != 0.0f;
+ /* { dg-final { scan-tree-dump "b_\[0-9] != 0;" "optimized" } } */
+}
+
+int foo3 (unsigned long long c) {
+ c &= 4095;
+ return (float) c != 0.0f;
+ /* { dg-final { scan-tree-dump "c_\[0-9] != 0;" "optimized" } } */
+}
+
+int foo4 (long long d) {
+ d &= 4095;
+ return (float) d != 0.0f;
+ /* { dg-final { scan-tree-dump "d_\[0-9] != 0;" "optimized" } } */
+}
+
+/* Tests when RHS is within range of integer type. */
+
+void in_range (unsigned int x)
+{
+ {
+ volatile int in_range_1;
+ x &= 4095;
+ in_range_1 = (float) x > 100.0f;
+ /* { dg-final { scan-tree-dump " > 100" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_2;
+ x &= 4095;
+ in_range_2 = (float) x < 200.0f;
+ /* { dg-final { scan-tree-dump " <= 199" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_3;
+ x &= 4095;
+ in_range_3 = (float) x > 300.5f;
+ /* { dg-final { scan-tree-dump " > 300" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_4;
+ x &= 4095;
+ in_range_4 = (float) x < 400.5f;
+ /* { dg-final { scan-tree-dump " <= 400" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_5;
+ x &= 4095;
+ in_range_5 = (float) x == 500.0f;
+ /* { dg-final { scan-tree-dump " == 500" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_6;
+ x &= 4095;
+ in_range_6 = (float) x != 600.0f;
+ /* { dg-final { scan-tree-dump " != 600" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_7;
+ x &= 4095;
+ in_range_7 = (float) x == 700.5f;
+ /* { dg-final { scan-tree-dump "in_range_7 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int in_range_8;
+ x &= 4095;
+ in_range_8 = (float) x != 800.5f;
+ /* { dg-final { scan-tree-dump "in_range_8 ={v} 1" "optimized" } } */
+ }
+}
+
+/* Tests for cases where RHS is out of range of integer type. */
+
+void out_range (unsigned int x)
+{
+ {
+ volatile int out_range_1;
+ x &= 4095;
+ out_range_1 = (float) x > -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_1 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_2;
+ x &= 4095;
+ out_range_2 = (float) x >= -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_2 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_3;
+ x &= 4095;
+ out_range_3 = (float) x < -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_3 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_4;
+ x &= 4095;
+ out_range_4 = (float) x <= -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_4 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_5;
+ x &= 4095;
+ out_range_5 = (float) x == -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_5 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int out_range_6;
+ x &= 4095;
+ out_range_6 = (float) x != -100.5f;
+ /* { dg-final { scan-tree-dump "out_range_6 ={v} 1" "optimized" } } */
+ }
+}
+
+/* Tests with non-finite float consts. */
+
+void nonfinite (unsigned int x)
+{
+#define INFINITY __builtin_inff ()
+
+ {
+ volatile int nonfinite_1;
+ x &= 4095;
+ nonfinite_1 = (float) x > INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_1 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_2;
+ x &= 4095;
+ nonfinite_2 = (float) x >= INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_2 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_3;
+ x &= 4095;
+ nonfinite_3 = (float) x < INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_3 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_4;
+ x &= 4095;
+ nonfinite_4 = (float) x <= INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_4 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_5;
+ x &= 4095;
+ nonfinite_5 = (float) x > -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_5 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_6;
+ x &= 4095;
+ nonfinite_6 = (float) x >= -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_6 ={v} 1" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_7;
+ x &= 4095;
+ nonfinite_7 = (float) x < -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_7 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_8;
+ x &= 4095;
+ nonfinite_8 = (float) x <= -INFINITY;
+ /* { dg-final { scan-tree-dump "nonfinite_8 ={v} 0" "optimized" } } */
+ }
+
+#define QNAN __builtin_nanf ("0")
+
+ /* Even for qNaNs, only == and != are quiet. */
+
+ {
+ volatile int nonfinite_9;
+ x &= 4095;
+ nonfinite_9 = (float) x == QNAN;
+ /* { dg-final { scan-tree-dump "nonfinite_9 ={v} 0" "optimized" } } */
+ }
+
+ {
+ volatile int nonfinite_10;
+ x &= 4095;
+ nonfinite_10 = (float) x != QNAN;
+ /* { dg-final { scan-tree-dump "nonfinite_10 ={v} 1" "optimized" } } */
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "\\(float\\)" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\(double\\)" "optimized" } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math" } */
+
+int foo1 (int a) {
+ return (float) a != 16777220.0f;
+}
+
+int foo2 (unsigned int b) {
+ return (float) b != 16777220.0f;
+}
+
+int foo3 (unsigned long long c) {
+ return (float) c != 16777220.0f;
+}
+
+int foo4 (long long d) {
+ return (float) d != 16777220.0f;
+}
+
+/* { dg-final { scan-tree-dump-times "\\(float\\)" 4 "optimized" } } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math" } */
+
+int foo1 (int a) {
+ return (float) a != 16777222.0f;
+ /* { dg-final { scan-tree-dump "a_\[0-9]\\(D\\) != 16777222;" "optimized" } } */
+}
+
+int foo2 (unsigned int b) {
+ return (float) b != 16777222.0f;
+ /* { dg-final { scan-tree-dump "b_\[0-9]\\(D\\) != 16777222;" "optimized" } } */
+}
+
+int foo3 (unsigned long long c) {
+ return (float) c != 16777222.0f;
+ /* { dg-final { scan-tree-dump "c_\[0-9]\\(D\\) != 16777222;" "optimized" } } */
+}
+
+int foo4 (long long d) {
+ return (float) d != 16777222.0f;
+ /* { dg-final { scan-tree-dump "d_\[0-9]\\(D\\) != 16777222;" "optimized" } } */
+}
+
+/* { dg-final { scan-tree-dump-not "\\(float\\)" "optimized" } } */