aboutsummaryrefslogtreecommitdiff
path: root/gcc/range-op.cc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2020-11-20 00:02:21 +0100
committerJakub Jelinek <jakub@redhat.com>2020-11-20 00:02:21 +0100
commitd3f293348768667c07770e433ff00af51fee73a2 (patch)
treee6772219b45c168901b54fe55de7384f1d27efbf /gcc/range-op.cc
parentd0d8b5d83614d8f0d0e40c0520d4f40ffa01f8d9 (diff)
downloadgcc-d3f293348768667c07770e433ff00af51fee73a2.zip
gcc-d3f293348768667c07770e433ff00af51fee73a2.tar.gz
gcc-d3f293348768667c07770e433ff00af51fee73a2.tar.bz2
ranger: Improve a % b operand ranges [PR91029]
As mentioned in the PR, the previous PR91029 patch was testing op2 >= 0 which is unnecessary, even negative op2 values will work the same, furthermore, from if a % b > 0 we can deduce a > 0 rather than just a >= 0 (0 % b would be 0), and it actually valid even for other constants than 0, a % b > 5 means a > 5 (a % b has the same sign as a and a in [0, 5] would result in a % b in [0, 5]. Also, we can deduce a range for the other operand, if we know a % b >= 20, then b must be (in absolute value for signed modulo) > 20, for a % [0, 20] the result would be [0, 19]. 2020-11-19 Jakub Jelinek <jakub@redhat.com> PR tree-optimization/91029 * range-op.cc (operator_trunc_mod::op1_range): Don't require signed types, nor require that op2 >= 0. Implement (a % b) >= x && x > 0 implies a >= x and (a % b) <= x && x < 0 implies a <= x. (operator_trunc_mod::op2_range): New method. * gcc.dg/tree-ssa/pr91029-1.c: New test. * gcc.dg/tree-ssa/pr91029-2.c: New test.
Diffstat (limited to 'gcc/range-op.cc')
-rw-r--r--gcc/range-op.cc69
1 files changed, 53 insertions, 16 deletions
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 5bf37e1..36f9fd6 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2645,6 +2645,9 @@ public:
virtual bool op1_range (irange &r, tree type,
const irange &lhs,
const irange &op2) const;
+ virtual bool op2_range (irange &r, tree type,
+ const irange &lhs,
+ const irange &op1) const;
} op_trunc_mod;
void
@@ -2694,24 +2697,58 @@ operator_trunc_mod::wi_fold (irange &r, tree type,
bool
operator_trunc_mod::op1_range (irange &r, tree type,
const irange &lhs,
- const irange &op2) const
+ const irange &) const
{
- // PR 91029. Check for signed truncation with op2 >= 0.
- if (TYPE_SIGN (type) == SIGNED && wi::ge_p (op2.lower_bound (), 0, SIGNED))
+ // PR 91029.
+ signop sign = TYPE_SIGN (type);
+ unsigned prec = TYPE_PRECISION (type);
+ // (a % b) >= x && x > 0 , then a >= x.
+ if (wi::gt_p (lhs.lower_bound (), 0, sign))
{
- unsigned prec = TYPE_PRECISION (type);
- // if a % b > 0 , then a >= 0.
- if (wi::gt_p (lhs.lower_bound (), 0, SIGNED))
- {
- r = value_range (type, wi::zero (prec), wi::max_value (prec, SIGNED));
- return true;
- }
- // if a % b < 0 , then a <= 0.
- if (wi::lt_p (lhs.upper_bound (), 0, SIGNED))
- {
- r = value_range (type, wi::min_value (prec, SIGNED), wi::zero (prec));
- return true;
- }
+ r = value_range (type, lhs.lower_bound (), wi::max_value (prec, sign));
+ return true;
+ }
+ // (a % b) <= x && x < 0 , then a <= x.
+ if (wi::lt_p (lhs.upper_bound (), 0, sign))
+ {
+ r = value_range (type, wi::min_value (prec, sign), lhs.upper_bound ());
+ return true;
+ }
+ return false;
+}
+
+bool
+operator_trunc_mod::op2_range (irange &r, tree type,
+ const irange &lhs,
+ const irange &) const
+{
+ // PR 91029.
+ signop sign = TYPE_SIGN (type);
+ unsigned prec = TYPE_PRECISION (type);
+ // (a % b) >= x && x > 0 , then b is in ~[-x, x] for signed
+ // or b > x for unsigned.
+ if (wi::gt_p (lhs.lower_bound (), 0, sign))
+ {
+ if (sign == SIGNED)
+ r = value_range (type, wi::neg (lhs.lower_bound ()),
+ lhs.lower_bound (), VR_ANTI_RANGE);
+ else if (wi::lt_p (lhs.lower_bound (), wi::max_value (prec, sign),
+ sign))
+ r = value_range (type, lhs.lower_bound () + 1,
+ wi::max_value (prec, sign));
+ else
+ return false;
+ return true;
+ }
+ // (a % b) <= x && x < 0 , then b is in ~[x, -x].
+ if (wi::lt_p (lhs.upper_bound (), 0, sign))
+ {
+ if (wi::gt_p (lhs.upper_bound (), wi::min_value (prec, sign), sign))
+ r = value_range (type, lhs.upper_bound (),
+ wi::neg (lhs.upper_bound ()), VR_ANTI_RANGE);
+ else
+ return false;
+ return true;
}
return false;
}