If there is a relation between op1 and op2, an unsigned minus operation
can sometimes do better than simply non-zero.
For unsigned, we can calculate more precise ranges for <, <=, > and >=, just
like we do for signed values.
I adjusted the testcase to also test the other unsigned relations variations.
Bootstrapped on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
Assisted-by: Claude Opus 5
PR tree-optimization/127915
gcc/
* range-op.cc (minus_op1_op2_relation_effect): Handle more
unsigned range restrictions with known relations.
gcc/testsuite/
* gcc.dg/pr127915.c: New.
---
gcc/range-op.cc | 45 +++++++++++++++++++++++++--
gcc/testsuite/gcc.dg/pr127915.c | 54 +++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr127915.c
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index d08193ce41d..c80d611f391 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2090,7 +2090,7 @@ operator_minus::lhs_op1_relation (const irange &, const
irange &op1,
bool
minus_op1_op2_relation_effect (irange &lhs_range, tree type,
- const irange &op1_range ATTRIBUTE_UNUSED,
+ const irange &op1_range,
const irange &op2_range ATTRIBUTE_UNUSED,
relation_kind rel)
{
@@ -2107,12 +2107,51 @@ minus_op1_op2_relation_effect (irange &lhs_range, tree
type,
else if (rel == VREL_NE)
rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec),
VR_ANTI_RANGE);
+ else if (TYPE_UNSIGNED (type))
+ {
+ wide_int ub1 = op1_range.upper_bound ();
+ wide_int ub2 = op2_range.upper_bound ();
+ switch (rel)
+ {
+ // op1 > op2, so the subtraction cannot wrap : [1, UB (op1)].
+ case VREL_GT:
+ // [0, 0] > op2 is undefined, so just return.
+ if (ub1 == 0)
+ return false;
+ rel_range = int_range<2> (type, wi::one (prec), ub1);
+ break;
+ // op1 >= op2 >= 0, so the subtraction cannot wrap : [0, UB (op1)].
+ case VREL_GE:
+ rel_range = int_range<2> (type, wi::zero (prec), ub1);
+ break;
+ // op1 < op2, so the subtraction always wraps : [-UB (op2), -1].
+ case VREL_LT:
+ // op1 < [0, 0] is undefined, so just return.
+ if (ub2 == 0)
+ return false;
+ rel_range = int_range<2> (type, wi::neg (ub2),
+ wi::minus_one (prec));
+ break;
+ // As VREL_LT, except that op1 == op2 also produces zero.
+ case VREL_LE:
+ // If UB (op2) is MAX_INT the result won't change, plus
+ // we dont want to construct an illegal [1, 0] range next.
+ if (ub2 == wi::max_value (prec, UNSIGNED))
+ return false;
+ rel_range = int_range<2> (type, wi::one (prec),
+ wi::sub (wi::minus_one (prec), ub2),
+ VR_ANTI_RANGE);
+ break;
+ default:
+ return false;
+ }
+ }
else if (TYPE_OVERFLOW_WRAPS (type))
{
switch (rel)
{
- // For wrapping signed values and unsigned, if op1 > op2 or
- // op1 < op2, then op1 - op2 can be restricted to ~[0, 0].
+ // For wrapping signed values , if op1 > op2 or op1 < op2,
+ // then op1 - op2 can be restricted to ~[0, 0].
case VREL_GT:
case VREL_LT:
rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec),
diff --git a/gcc/testsuite/gcc.dg/pr127915.c b/gcc/testsuite/gcc.dg/pr127915.c
new file mode 100644
index 00000000000..cd8650b6efc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr127915.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+/* last >= first and last <= 100, so last - first is [0, 100]. */
+void
+f1 (unsigned first, unsigned last)
+{
+ if (first > last || last > 100)
+ __builtin_unreachable ();
+
+ unsigned t = last - first;
+ if (t > 100)
+ __builtin_trap ();
+}
+
+/* last > first and last <= 100, so last - first is [1, 100]. */
+void
+f2 (unsigned first, unsigned last)
+{
+ if (first >= last || last > 100)
+ __builtin_unreachable ();
+
+ unsigned t = last - first;
+ if (t == 0 || t > 100)
+ __builtin_trap ();
+}
+
+/* first < last and last <= 100. The subtraction always wraps, so
+ first - last is [-100, -1]. */
+void
+f3 (unsigned first, unsigned last)
+{
+ if (first >= last || last > 100)
+ __builtin_unreachable ();
+
+ unsigned t = first - last;
+ if (t < (unsigned) -100)
+ __builtin_trap ();
+}
+
+/* first <= last and last <= 100. As f3, except that first == last also
+ produces zero, giving ~[1, -101]. */
+void
+f4 (unsigned first, unsigned last)
+{
+ if (first > last || last > 100)
+ __builtin_unreachable ();
+
+ unsigned t = first - last;
+ if (t != 0 && t < (unsigned) -100)
+ __builtin_trap ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_trap" "evrp" } } */
--
2.56.0