From 6c022f732a934b61ab3fc7312bd3f2fc725d4223 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Tue, 14 Feb 2017 08:54:09 -0700 Subject: [PATCH] re PR tree-optimization/79095 (spurious stringop-overflow warning) PR tree-optimization/79095 * tree-vrp.c (extract_range_from_binary_expr_1): For EXACT_DIV_EXPR, if the numerator has the range ~[0,0] make the resultant range ~[0,0]. (extract_range_from_binary_expr): For MINUS_EXPR with no derived range, if the operands are known to be not equal, then the resulting range is ~[0,0]. (intersect_ranges): If the new range is ~[0,0] and the old range is wide, then prefer ~[0,0]. * tree-vrp.c (overflow_comparison_p_1): New function. (overflow_comparison_p): New function. * tree-vrp.c (register_edge_assert_for_2): Register additional asserts if NAME is used in an overflow test. (vrp_evaluate_conditional_warnv_with_ops): If the ops represent an overflow check that can be expressed as an equality test, then adjust ops to be that equality test. PR tree-optimization/79095 * g++.dg/pr79095-1.C: New test * g++.dg/pr79095-2.C: New test * g++.dg/pr79095-3.C: New test * g++.dg/pr79095-4.C: New test * g++.dg/pr79095-5.C: New test * gcc.c-torture/execute/arith-1.c: Update with more cases. * gcc.dg/tree-ssa/pr79095-1.c: New test. From-SVN: r245434 --- gcc/ChangeLog | 18 ++ gcc/testsuite/ChangeLog | 11 + gcc/testsuite/g++.dg/pr79095-1.C | 40 +++ gcc/testsuite/g++.dg/pr79095-2.C | 46 +++ gcc/testsuite/g++.dg/pr79095-3.C | 17 + gcc/testsuite/g++.dg/pr79095-4.C | 26 ++ gcc/testsuite/g++.dg/pr79095-5.C | 34 ++ gcc/testsuite/gcc.c-torture/execute/arith-1.c | 32 ++ gcc/testsuite/gcc.dg/tree-ssa/pr79095.c | 436 ++++++++++++++++++++++++++ gcc/tree-vrp.c | 196 +++++++++++- 10 files changed, 855 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/pr79095-1.C create mode 100644 gcc/testsuite/g++.dg/pr79095-2.C create mode 100644 gcc/testsuite/g++.dg/pr79095-3.C create mode 100644 gcc/testsuite/g++.dg/pr79095-4.C create mode 100644 gcc/testsuite/g++.dg/pr79095-5.C create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr79095.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 09970b4..ba94c53 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2017-02-14 Jeff Law + + PR tree-optimization/79095 + * tree-vrp.c (extract_range_from_binary_expr_1): For EXACT_DIV_EXPR, + if the numerator has the range ~[0,0] make the resultant range ~[0,0]. + (extract_range_from_binary_expr): For MINUS_EXPR with no derived range, + if the operands are known to be not equal, then the resulting range + is ~[0,0]. + (intersect_ranges): If the new range is ~[0,0] and the old range is + wide, then prefer ~[0,0]. + * tree-vrp.c (overflow_comparison_p_1): New function. + (overflow_comparison_p): New function. + * tree-vrp.c (register_edge_assert_for_2): Register additional asserts + if NAME is used in an overflow test. + (vrp_evaluate_conditional_warnv_with_ops): If the ops represent an + overflow check that can be expressed as an equality test, then adjust + ops to be that equality test. + 2017-02-14 Andreas Krebbel * config/s390/s390-builtin-types.def: Remove flags argument. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6f6f0da..fb5b69d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2017-02-14 Jeff Law + + PR tree-optimization/79095 + * g++.dg/pr79095-1.C: New test + * g++.dg/pr79095-2.C: New test + * g++.dg/pr79095-3.C: New test + * g++.dg/pr79095-4.C: New test + * g++.dg/pr79095-5.C: New test + * gcc.c-torture/execute/arith-1.c: Update with more cases. + * gcc.dg/tree-ssa/pr79095-1.c: New test. + 2017-02-14 James Greenhalgh * gcc.target/aarch64/vect_fp16_1.c: New. diff --git a/gcc/testsuite/g++.dg/pr79095-1.C b/gcc/testsuite/g++.dg/pr79095-1.C new file mode 100644 index 0000000..4b8043c --- /dev/null +++ b/gcc/testsuite/g++.dg/pr79095-1.C @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall -O3" } */ + +typedef long unsigned int size_t; + +inline void +fill (int *p, size_t n, int) +{ + while (n--) + *p++ = 0; +} + +struct B +{ + int* p0, *p1, *p2; + + size_t size () const { + return size_t (p1 - p0); + } + + void resize (size_t n) { + if (n > size()) + append (n - size()); + } + + void append (size_t n) + { + if (size_t (p2 - p1) >= n) { + fill (p1, n, 0); + } + } +}; + +void foo (B &b) +{ + if (b.size () != 0) + b.resize (b.size () - 1); +} + + diff --git a/gcc/testsuite/g++.dg/pr79095-2.C b/gcc/testsuite/g++.dg/pr79095-2.C new file mode 100644 index 0000000..9dabc7e --- /dev/null +++ b/gcc/testsuite/g++.dg/pr79095-2.C @@ -0,0 +1,46 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall -O3" } */ + +typedef long unsigned int size_t; + +inline void +fill (int *p, size_t n, int) +{ + while (n--) + *p++ = 0; +} + +struct B +{ + int* p0, *p1, *p2; + + size_t size () const { + return size_t (p1 - p0); + } + + void resize (size_t n) { + if (n > size()) + append (n - size()); + } + + void append (size_t n) + { + if (size_t (p2 - p1) >= n) { + fill (p1, n, 0); + } + } +}; + +void foo (B &b) +{ + b.resize (b.size () - 1); +} + +/* If b.size() == 0, then the argument to b.resize is -1U (it overflowed). + This will result calling "fill" which turns into a memset with a bogus + length argument. We want to make sure we warn, which multiple + things. First the ldist pass converted the loop into a memset, + cprop and simplifications made the length a constant and the static + analysis pass determines it's a bogus size to pass to memset. */ +/* { dg-warning "exceeds maximum object size" "" { target *-*-* } 0 } */ + diff --git a/gcc/testsuite/g++.dg/pr79095-3.C b/gcc/testsuite/g++.dg/pr79095-3.C new file mode 100644 index 0000000..28c8a37 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr79095-3.C @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall -O3" } */ + +#include + +void foo(std::vector &v); + +void vtest() +{ + std::vector v; + foo (v); + if (v.size() > 0) + { + v.resize (v.size()-1); + } +} + diff --git a/gcc/testsuite/g++.dg/pr79095-4.C b/gcc/testsuite/g++.dg/pr79095-4.C new file mode 100644 index 0000000..df55025 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr79095-4.C @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall -O3 -fdump-tree-vrp2" } */ + +#include + +void foo(std::vector &v); + +void vtest() +{ + std::vector v; + foo (v); + { + v.resize (v.size()-1); + } +} + +/* As written this testcase should trigger a warning. We overflow to -1U + if v.size() == 0 in foo(). This results in bogus calls to memset. + + The number of clearing loops in the IL can vary depending on the C++ + mode used for the test. But by the end of VRP2, there should be a single + clearing loop left and it should be using memcpy. */ +/* { dg-final { scan-tree-dump-times "__builtin_memset \\(_\[0-9\]+, 0, \[0-9\]+\\)" 1 "vrp2" } } */ + +/* And that call should trigger a warning. */ +/* { dg-warning "exceeds maximum object size" "" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/g++.dg/pr79095-5.C b/gcc/testsuite/g++.dg/pr79095-5.C new file mode 100644 index 0000000..266f4e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr79095-5.C @@ -0,0 +1,34 @@ +/* { dg-do compile } */ +/* { dg-options "-Wall -O3" } */ + +typedef __SIZE_TYPE__ size_t; + +struct S { + int *p0, *p1, *p2; + + size_t size () const { return p1 - p0; } + + void f (size_t n) { + if (n > size ()) // can't happen because + foo (n - size ()); // n is in [1, MIN(size() - 1, 3)] + else if (n < size ()) + bar (p0 + n); + } + + void foo (size_t n) + { + size_t left = (size_t)(p2 - p1); + if (left >= n) + __builtin_memset (p2, 0, n * sizeof *p2); // { dg-bogus "maximum object size" } + + } + + void bar (int*); +}; + +void f (S &s) +{ + size_t n = s.size (); + if (n > 1 && n < 5) + s.f (n - 1); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/arith-1.c b/gcc/testsuite/gcc.c-torture/execute/arith-1.c index 58df322..6168d77 100644 --- a/gcc/testsuite/gcc.c-torture/execute/arith-1.c +++ b/gcc/testsuite/gcc.c-torture/execute/arith-1.c @@ -7,9 +7,41 @@ sat_add (unsigned i) return ret; } +unsigned +sat_add2 (unsigned i) +{ + unsigned ret = i + 1; + if (ret > i) + return ret; + return i; +} + +unsigned +sat_add3 (unsigned i) +{ + unsigned ret = i - 1; + if (ret > i) + ret = i; + return ret; +} + +unsigned +sat_add4 (unsigned i) +{ + unsigned ret = i - 1; + if (ret < i) + return ret; + return i; +} main () { if (sat_add (~0U) != ~0U) abort (); + if (sat_add2 (~0U) != ~0U) + abort (); + if (sat_add3 (0U) != 0U) + abort (); + if (sat_add4 (0U) != 0U) + abort (); exit (0); } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79095.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79095.c new file mode 100644 index 0000000..f635fca --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79095.c @@ -0,0 +1,436 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-vrp1" } */ + +extern void arf (unsigned x, unsigned y); +extern void baz (unsigned x, unsigned y); + +unsigned +f1 (unsigned a, unsigned b) +{ + b = a + 1; + if (b < a) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + + +unsigned +f1r (unsigned a, unsigned b) +{ + b = a + 1; + if (a < b) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f1n (unsigned a, unsigned b) +{ + b = a + 1; + if (!(b < a)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f1nr (unsigned a, unsigned b) +{ + b = a + 1; + if (!(a < b)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + + +unsigned +f1o (unsigned a, unsigned b) +{ + b = a + 1; + if (a < b) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f1ro (unsigned a, unsigned b) +{ + b = a + 1; + if (b < a) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f1no (unsigned a, unsigned b) +{ + b = a + 1; + if (!(a < b)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f1nro (unsigned a, unsigned b) +{ + b = a + 1; + if (!(b < a)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + + +unsigned +f2 (unsigned a, unsigned b) +{ + b = a + 1; + if (b <= a) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f2r (unsigned a, unsigned b) +{ + b = a + 1; + if (a <= b) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f2n (unsigned a, unsigned b) +{ + b = a + 1; + if (!(b <= a)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f2nr (unsigned a, unsigned b) +{ + b = a + 1; + if (!(a <= b)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + + +unsigned +f2o (unsigned a, unsigned b) +{ + b = a + 1; + if (a <= b) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f2ro (unsigned a, unsigned b) +{ + b = a + 1; + if (b <= a) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f2no (unsigned a, unsigned b) +{ + b = a + 1; + if (!(a <= b)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f2nro (unsigned a, unsigned b) +{ + b = a + 1; + if (!(b <= a)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + + +unsigned +f3 (unsigned a, unsigned b) +{ + b = a - 1; + if (b < a) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f3r (unsigned a, unsigned b) +{ + b = a - 1; + if (a < b) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f3n (unsigned a, unsigned b) +{ + b = a - 1; + if (!(b < a)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f3nr (unsigned a, unsigned b) +{ + b = a - 1; + if (!(a < b)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + + +unsigned +f3o (unsigned a, unsigned b) +{ + b = a - 1; + if (a < b) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f3ro (unsigned a, unsigned b) +{ + b = a - 1; + if (b < a) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f3no (unsigned a, unsigned b) +{ + b = a - 1; + if (!(a < b)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f3nro (unsigned a, unsigned b) +{ + b = a - 1; + if (!(b < a)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + + +unsigned +f4 (unsigned a, unsigned b) +{ + b = a - 1; + if (b <= a) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f4r (unsigned a, unsigned b) +{ + b = a - 1; + if (a <= b) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f4n (unsigned a, unsigned b) +{ + b = a - 1; + if (!(b <= a)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f4nr (unsigned a, unsigned b) +{ + b = a - 1; + if (!(a <= b)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + + +unsigned +f4o (unsigned a, unsigned b) +{ + b = a - 1; + if (a <= b) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +unsigned +f4ro (unsigned a, unsigned b) +{ + b = a - 1; + if (b <= a) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f4no (unsigned a, unsigned b) +{ + b = a - 1; + if (!(a <= b)) + { + baz (a, b); + return 42; + } + arf (a, b); + return b; +} + +unsigned +f4nro (unsigned a, unsigned b) +{ + b = a - 1; + if (!(b <= a)) + { + arf (a, b); + return 42; + } + baz (a, b); + return b; +} + +/* All calls to baz should still reference a & b as arguments. */ +/* { dg-final { scan-tree-dump-times "baz \\(a_\[0-9\]+\\(D\\), b_\[0-9\]+\\)" 32 "vrp1"} } */ + + +/* All calls to arf should have constant arguments. */ +/* { dg-final { scan-tree-dump-times "arf \\(\[0-9\]+, \[0-9\]+\\)" 32 "vrp1"} } */ diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 9ffecc9..95bf1cf 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -2259,6 +2259,19 @@ extract_range_from_binary_expr_1 (value_range *vr, else if (vr1.type == VR_UNDEFINED) set_value_range_to_varying (&vr1); + /* We get imprecise results from ranges_from_anti_range when + code is EXACT_DIV_EXPR. We could mask out bits in the resulting + range, but then we also need to hack up vrp_meet. It's just + easier to special case when vr0 is ~[0,0] for EXACT_DIV_EXPR. */ + if (code == EXACT_DIV_EXPR + && vr0.type == VR_ANTI_RANGE + && vr0.min == vr0.max + && integer_zerop (vr0.min)) + { + set_value_range_to_nonnull (vr, expr_type); + return; + } + /* Now canonicalize anti-ranges to ranges when they are not symbolic and express ~[] op X as ([]' op X) U ([]'' op X). */ if (vr0.type == VR_ANTI_RANGE @@ -3298,6 +3311,21 @@ extract_range_from_binary_expr (value_range *vr, extract_range_from_binary_expr_1 (vr, code, expr_type, &n_vr0, &vr1); } + + /* If we didn't derive a range for MINUS_EXPR, and + op1's range is ~[op0,op0] or vice-versa, then we + can derive a non-null range. This happens often for + pointer subtraction. */ + if (vr->type == VR_VARYING + && code == MINUS_EXPR + && TREE_CODE (op0) == SSA_NAME + && ((vr0.type == VR_ANTI_RANGE + && vr0.min == op1 + && vr0.min == vr0.max) + || (vr1.type == VR_ANTI_RANGE + && vr1.min == op0 + && vr1.min == vr1.max))) + set_value_range_to_nonnull (vr, TREE_TYPE (op0)); } /* Extract range information from a unary operation CODE based on @@ -5158,6 +5186,118 @@ masked_increment (const wide_int &val_in, const wide_int &mask, return val ^ sgnbit; } +/* Helper for overflow_comparison_p + + OP0 CODE OP1 is a comparison. Examine the comparison and potentially + OP1's defining statement to see if it ultimately has the form + OP0 CODE (OP0 PLUS INTEGER_CST) + + If so, return TRUE indicating this is an overflow test and store into + *NEW_CST an updated constant that can be used in a narrowed range test. + + REVERSED indicates if the comparison was originally: + + OP1 CODE' OP0. + + This affects how we build the updated constant. */ + +static bool +overflow_comparison_p_1 (enum tree_code code, tree op0, tree op1, + bool follow_assert_exprs, bool reversed, tree *new_cst) +{ + /* See if this is a relational operation between two SSA_NAMES with + unsigned, overflow wrapping values. If so, check it more deeply. */ + if ((code == LT_EXPR || code == LE_EXPR + || code == GE_EXPR || code == GT_EXPR) + && TREE_CODE (op0) == SSA_NAME + && TREE_CODE (op1) == SSA_NAME + && INTEGRAL_TYPE_P (TREE_TYPE (op0)) + && TYPE_UNSIGNED (TREE_TYPE (op0)) + && TYPE_OVERFLOW_WRAPS (TREE_TYPE (op0))) + { + gimple *op1_def = SSA_NAME_DEF_STMT (op1); + + /* If requested, follow any ASSERT_EXPRs backwards for OP1. */ + if (follow_assert_exprs) + { + while (gimple_assign_single_p (op1_def) + && TREE_CODE (gimple_assign_rhs1 (op1_def)) == ASSERT_EXPR) + { + op1 = TREE_OPERAND (gimple_assign_rhs1 (op1_def), 0); + if (TREE_CODE (op1) != SSA_NAME) + break; + op1_def = SSA_NAME_DEF_STMT (op1); + } + } + + /* Now look at the defining statement of OP1 to see if it adds + or subtracts a nonzero constant from another operand. */ + if (op1_def + && is_gimple_assign (op1_def) + && gimple_assign_rhs_code (op1_def) == PLUS_EXPR + && TREE_CODE (gimple_assign_rhs2 (op1_def)) == INTEGER_CST + && !integer_zerop (gimple_assign_rhs2 (op1_def))) + { + tree target = gimple_assign_rhs1 (op1_def); + + /* If requested, follow ASSERT_EXPRs backwards for op0 looking + for one where TARGET appears on the RHS. */ + if (follow_assert_exprs) + { + /* Now see if that "other operand" is op0, following the chain + of ASSERT_EXPRs if necessary. */ + gimple *op0_def = SSA_NAME_DEF_STMT (op0); + while (op0 != target + && gimple_assign_single_p (op0_def) + && TREE_CODE (gimple_assign_rhs1 (op0_def)) == ASSERT_EXPR) + { + op0 = TREE_OPERAND (gimple_assign_rhs1 (op0_def), 0); + if (TREE_CODE (op0) != SSA_NAME) + break; + op0_def = SSA_NAME_DEF_STMT (op0); + } + } + + /* If we did not find our target SSA_NAME, then this is not + an overflow test. */ + if (op0 != target) + return false; + + tree type = TREE_TYPE (op0); + wide_int max = wi::max_value (TYPE_PRECISION (type), UNSIGNED); + tree inc = gimple_assign_rhs2 (op1_def); + if (reversed) + *new_cst = wide_int_to_tree (type, max + inc); + else + *new_cst = wide_int_to_tree (type, max - inc); + return true; + } + } + return false; +} + +/* OP0 CODE OP1 is a comparison. Examine the comparison and potentially + OP1's defining statement to see if it ultimately has the form + OP0 CODE (OP0 PLUS INTEGER_CST) + + If so, return TRUE indicating this is an overflow test and store into + *NEW_CST an updated constant that can be used in a narrowed range test. + + These statements are left as-is in the IL to facilitate discovery of + {ADD,SUB}_OVERFLOW sequences later in the optimizer pipeline. But + the alternate range representation is often useful within VRP. */ + +static bool +overflow_comparison_p (tree_code code, tree name, tree val, + bool use_equiv_p, tree *new_cst) +{ + if (overflow_comparison_p_1 (code, name, val, use_equiv_p, false, new_cst)) + return true; + return overflow_comparison_p_1 (swap_tree_comparison (code), val, name, + use_equiv_p, true, new_cst); +} + + /* Try to register an edge assertion for SSA name NAME on edge E for the condition COND contributing to the conditional jump pointed to by BSI. Invert the condition COND if INVERT is true. */ @@ -5179,7 +5319,17 @@ register_edge_assert_for_2 (tree name, edge e, gimple_stmt_iterator bsi, /* Only register an ASSERT_EXPR if NAME was found in the sub-graph reachable from E. */ if (live_on_edge (e, name)) - register_new_assert_for (name, name, comp_code, val, NULL, e, bsi); + { + tree x; + if (overflow_comparison_p (comp_code, name, val, false, &x)) + { + enum tree_code new_code + = ((comp_code == GT_EXPR || comp_code == GE_EXPR) + ? GT_EXPR : LE_EXPR); + register_new_assert_for (name, name, new_code, x, NULL, e, bsi); + } + register_new_assert_for (name, name, comp_code, val, NULL, e, bsi); + } /* In the case of NAME <= CST and NAME being defined as NAME = (unsigned) NAME2 + CST2 we can assert NAME2 >= -CST2 @@ -7538,6 +7688,39 @@ vrp_evaluate_conditional_warnv_with_ops (enum tree_code code, tree op0, && !POINTER_TYPE_P (TREE_TYPE (op0))) return NULL_TREE; + /* If OP0 CODE OP1 is an overflow comparison, if it can be expressed + as a simple equality test, then prefer that over its current form + for evaluation. + + An overflow test which collapses to an equality test can always be + expressed as a comparison of one argument against zero. Overflow + occurs when the chosen argument is zero and does not occur if the + chosen argument is not zero. */ + tree x; + if (overflow_comparison_p (code, op0, op1, use_equiv_p, &x)) + { + wide_int max = wi::max_value (TYPE_PRECISION (TREE_TYPE (op0)), UNSIGNED); + /* B = A - 1; if (A < B) -> B = A - 1; if (A == 0) + B = A - 1; if (A > B) -> B = A - 1; if (A != 0) + B = A + 1; if (B < A) -> B = A + 1; if (B == 0) + B = A + 1; if (B > A) -> B = A + 1; if (B != 0) */ + if (integer_zerop (x)) + { + op1 = x; + code = (code == LT_EXPR || code == LE_EXPR) ? EQ_EXPR : NE_EXPR; + } + /* B = A + 1; if (A > B) -> B = A + 1; if (B == 0) + B = A + 1; if (A < B) -> B = A + 1; if (B != 0) + B = A - 1; if (B > A) -> B = A - 1; if (A == 0) + B = A - 1; if (B < A) -> B = A - 1; if (A != 0) */ + else if (wi::eq_p (x, max - 1)) + { + op0 = op1; + op1 = wide_int_to_tree (TREE_TYPE (op0), 0); + code = (code == GT_EXPR || code == GE_EXPR) ? EQ_EXPR : NE_EXPR; + } + } + if ((ret = vrp_evaluate_conditional_warnv_with_ops_using_ranges (code, op0, op1, strict_overflow_p))) return ret; @@ -8620,6 +8803,17 @@ intersect_ranges (enum value_range_type *vr0type, else if (vrp_val_is_min (vr1min) && vrp_val_is_max (vr1max)) ; + /* Choose the anti-range if it is ~[0,0], that range is special + enough to special case when vr1's range is relatively wide. */ + else if (*vr0min == *vr0max + && integer_zerop (*vr0min) + && (TYPE_PRECISION (TREE_TYPE (*vr0min)) + == TYPE_PRECISION (ptr_type_node)) + && TREE_CODE (vr1max) == INTEGER_CST + && TREE_CODE (vr1min) == INTEGER_CST + && (wi::clz (wi::sub (vr1max, vr1min)) + < TYPE_PRECISION (TREE_TYPE (*vr0min)) / 2)) + ; /* Else choose the range. */ else { -- 2.7.4