On Thu, Nov 12, 2026 at 12:57 PM Andrew MacLeod <[email protected]> wrote: > > 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.
See https://inbox.sourceware.org/gcc-patches/[email protected]/ and https://inbox.sourceware.org/gcc-patches/[email protected]/ for 2 related patches that need to be approved. > 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 >
