@@ -2679,6 +2679,27 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(le (minus (convert:etype @0) { lo; }) { hi; })
(gt (minus (convert:etype @0) { lo; }) { hi; })))))))))
+#if GIMPLE
+/* X /[ex] Y cmp Z -> X cmp (Y * Z), if Y * Z is representable. */
+(for cmp (simple_comparison)
+ (simplify
+ (cmp (exact_div:s @0 @1) @2)
+ (with { int_range_max r1, r2; }
+ (if (INTEGRAL_TYPE_P (type)
+ && get_range_query (cfun)->range_of_expr (r1, @1)
+ && get_range_query (cfun)->range_of_expr (r2, @2)
+ && range_op_handler (MULT_EXPR).fits_type_p (r1, r2))
+ (cmp @0 (mult @1 @2)))))
+ (simplify
+ (cmp @2 (exact_div:s @0 @1))
+ (with { int_range_max r1, r2; }
+ (if (INTEGRAL_TYPE_P (type)
+ && get_range_query (cfun)->range_of_expr (r1, @1)
+ && get_range_query (cfun)->range_of_expr (r2, @2)
+ && range_op_handler (MULT_EXPR).fits_type_p (r1, r2))
+ (cmp (mult @1 @2) @0)))))
+#endif
+
/* X + Z < Y + Z is the same as X < Y when there is no overflow. */
(for op (lt le ge gt)
(simplify
@@ -539,7 +539,8 @@ public:
const irange &rh) const final override;
virtual bool overflow_free_p (const irange &lh, const irange &rh,
- relation_trio = TRIO_VARYING) const;
+ relation_trio = TRIO_VARYING,
+ range_query_type = QUERY_WITH_GIMPLE_UB) const;
// Check compatibility of all operands.
bool operand_check_p (tree t1, tree t2, tree t3) const final override
{ return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
@@ -615,7 +616,8 @@ public:
const irange &rh) const final override;
virtual bool overflow_free_p (const irange &lh, const irange &rh,
- relation_trio = TRIO_VARYING) const;
+ relation_trio = TRIO_VARYING,
+ range_query_type = QUERY_WITH_GIMPLE_UB) const;
// Check compatibility of all operands.
bool operand_check_p (tree t1, tree t2, tree t3) const final override
{ return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
@@ -701,7 +703,8 @@ public:
const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub,
relation_kind kind) const final override;
virtual bool overflow_free_p (const irange &lh, const irange &rh,
- relation_trio = TRIO_VARYING) const;
+ relation_trio = TRIO_VARYING,
+ range_query_type = QUERY_WITH_GIMPLE_UB) const;
// Check compatibility of all operands.
bool operand_check_p (tree t1, tree t2, tree t3) const final override
{ return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
@@ -470,7 +470,8 @@ range_op_handler::op1_op2_relation (const vrange &lhs,
bool
range_op_handler::overflow_free_p (const vrange &lh,
const vrange &rh,
- relation_trio rel) const
+ relation_trio rel,
+ range_query_type query_type) const
{
gcc_checking_assert (m_operator);
switch (dispatch_kind (lh, lh, rh))
@@ -478,7 +479,7 @@ range_op_handler::overflow_free_p (const vrange &lh,
case RO_III:
return m_operator->overflow_free_p(as_a <irange> (lh),
as_a <irange> (rh),
- rel);
+ rel, query_type);
default:
return false;
}
@@ -823,7 +824,7 @@ range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED,
bool
range_operator::overflow_free_p (const irange &, const irange &,
- relation_trio) const
+ relation_trio, range_query_type) const
{
return false;
}
@@ -4532,13 +4533,13 @@ range_op_table::initialize_integral_ops ()
bool
operator_plus::overflow_free_p (const irange &lh, const irange &rh,
- relation_trio) const
+ relation_trio, range_query_type query_type) const
{
if (lh.undefined_p () || rh.undefined_p ())
return false;
tree type = lh.type ();
- if (TYPE_OVERFLOW_UNDEFINED (type))
+ if (query_type == QUERY_WITH_GIMPLE_UB && TYPE_OVERFLOW_UNDEFINED (type))
return true;
wi::overflow_type ovf;
@@ -4563,13 +4564,13 @@ operator_plus::overflow_free_p (const irange &lh, const irange &rh,
bool
operator_minus::overflow_free_p (const irange &lh, const irange &rh,
- relation_trio) const
+ relation_trio, range_query_type query_type) const
{
if (lh.undefined_p () || rh.undefined_p ())
return false;
tree type = lh.type ();
- if (TYPE_OVERFLOW_UNDEFINED (type))
+ if (query_type == QUERY_WITH_GIMPLE_UB && TYPE_OVERFLOW_UNDEFINED (type))
return true;
wi::overflow_type ovf;
@@ -4594,13 +4595,13 @@ operator_minus::overflow_free_p (const irange &lh, const irange &rh,
bool
operator_mult::overflow_free_p (const irange &lh, const irange &rh,
- relation_trio) const
+ relation_trio, range_query_type query_type) const
{
if (lh.undefined_p () || rh.undefined_p ())
return false;
tree type = lh.type ();
- if (TYPE_OVERFLOW_UNDEFINED (type))
+ if (query_type == QUERY_WITH_GIMPLE_UB && TYPE_OVERFLOW_UNDEFINED (type))
return true;
wi::overflow_type ovf;
@@ -32,6 +32,18 @@ enum range_op_dispatch_type
DISPATCH_OP1_OP2_RELATION
};
+enum range_query_type
+{
+ // Take advantage of gimple's rules about undefined behavior. This is
+ // usually the right choice when querying existing operations.
+ QUERY_WITH_GIMPLE_UB,
+
+ // The result of the operation must follow the rules of natural arithmetic.
+ // This is usually the right choice when querying whether we can create
+ // a new operation.
+ QUERY_NATURAL_ARITH
+};
+
// This class is implemented for each kind of operator supported by
// the range generator. It serves various purposes.
//
@@ -225,7 +237,8 @@ public:
const frange &op2) const;
virtual bool overflow_free_p (const irange &lh, const irange &rh,
- relation_trio = TRIO_VARYING) const;
+ relation_trio = TRIO_VARYING,
+ range_query_type = QUERY_WITH_GIMPLE_UB) const;
// Compatability check for operands.
virtual bool operand_check_p (tree, tree, tree) const;
@@ -319,7 +332,10 @@ public:
const vrange &op1,
const vrange &op2) const;
bool overflow_free_p (const vrange &lh, const vrange &rh,
- relation_trio = TRIO_VARYING) const;
+ relation_trio = TRIO_VARYING,
+ range_query_type = QUERY_WITH_GIMPLE_UB) const;
+ bool fits_type_p (const irange &lh, const irange &rh,
+ relation_trio = TRIO_VARYING) const;
bool operand_check_p (tree, tree, tree) const;
protected:
unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
@@ -330,6 +346,17 @@ protected:
range_operator *m_operator;
};
+// Test whether the result of the operator fits within the type.
+// This follows the rules of natural arithmetic and so disallows
+// any form of overflow or wrapping.
+
+inline bool
+range_op_handler::fits_type_p (const irange &lh, const irange &rh,
+ relation_trio trio) const
+{
+ return overflow_free_p (lh, rh, trio, QUERY_NATURAL_ARITH);
+}
+
// Cast the range in R to TYPE if R supports TYPE.
inline bool
new file mode 100644
@@ -0,0 +1,21 @@
+/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
+
+#define TEST_CMP(FN, OP) \
+ int \
+ FN (int x, int y) \
+ { \
+ if (x & 7) \
+ __builtin_unreachable (); \
+ x /= 4; \
+ return x OP (int) (y & (~0U >> 3)); \
+ }
+
+TEST_CMP (f1, <)
+TEST_CMP (f2, <=)
+TEST_CMP (f3, ==)
+TEST_CMP (f4, !=)
+TEST_CMP (f5, >=)
+TEST_CMP (f6, >)
+
+/* { dg-final { scan-tree-dump-not {<[a-z]*_div_expr, } "optimized" } } */
+/* { dg-final { scan-tree-dump-not {<rshift_expr, } "optimized" } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
+
+#define TEST_CMP(FN, OP) \
+ int \
+ FN (int x, int y) \
+ { \
+ if (x & 7) \
+ __builtin_unreachable (); \
+ x /= 4; \
+ return x OP (int) (y & (~0U >> 2)); \
+ }
+
+TEST_CMP (f1, <)
+TEST_CMP (f2, <=)
+TEST_CMP (f3, ==)
+TEST_CMP (f4, !=)
+TEST_CMP (f5, >=)
+TEST_CMP (f6, >)
+
+/* { dg-final { scan-tree-dump-times {<[a-z]*_div_expr, } 6 "optimized" } } */