Commit 6c022f73 by Jeff Law Committed by Jeff Law

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
parent c145a510
2017-02-14 Jeff Law <law@redhat.com>
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 <krebbel@linux.vnet.ibm.com> 2017-02-14 Andreas Krebbel <krebbel@linux.vnet.ibm.com>
* config/s390/s390-builtin-types.def: Remove flags argument. * config/s390/s390-builtin-types.def: Remove flags argument.
......
2017-02-14 Jeff Law <law@redhat.com>
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 <james.greenhalgh@arm.com> 2017-02-14 James Greenhalgh <james.greenhalgh@arm.com>
* gcc.target/aarch64/vect_fp16_1.c: New. * gcc.target/aarch64/vect_fp16_1.c: New.
......
/* { 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);
}
/* { 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 } */
/* { dg-do compile } */
/* { dg-options "-Wall -O3" } */
#include <vector>
void foo(std::vector<unsigned int> &v);
void vtest()
{
std::vector<unsigned int> v;
foo (v);
if (v.size() > 0)
{
v.resize (v.size()-1);
}
}
/* { dg-do compile } */
/* { dg-options "-Wall -O3 -fdump-tree-vrp2" } */
#include <vector>
void foo(std::vector<unsigned int> &v);
void vtest()
{
std::vector<unsigned int> 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 } */
/* { 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);
}
...@@ -7,9 +7,41 @@ sat_add (unsigned i) ...@@ -7,9 +7,41 @@ sat_add (unsigned i)
return ret; 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 () main ()
{ {
if (sat_add (~0U) != ~0U) if (sat_add (~0U) != ~0U)
abort (); abort ();
if (sat_add2 (~0U) != ~0U)
abort ();
if (sat_add3 (0U) != 0U)
abort ();
if (sat_add4 (0U) != 0U)
abort ();
exit (0); exit (0);
} }
/* { 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"} } */
...@@ -2259,6 +2259,19 @@ extract_range_from_binary_expr_1 (value_range *vr, ...@@ -2259,6 +2259,19 @@ extract_range_from_binary_expr_1 (value_range *vr,
else if (vr1.type == VR_UNDEFINED) else if (vr1.type == VR_UNDEFINED)
set_value_range_to_varying (&vr1); 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 /* Now canonicalize anti-ranges to ranges when they are not symbolic
and express ~[] op X as ([]' op X) U ([]'' op X). */ and express ~[] op X as ([]' op X) U ([]'' op X). */
if (vr0.type == VR_ANTI_RANGE if (vr0.type == VR_ANTI_RANGE
...@@ -3298,6 +3311,21 @@ extract_range_from_binary_expr (value_range *vr, ...@@ -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); 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 /* 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, ...@@ -5158,6 +5186,118 @@ masked_increment (const wide_int &val_in, const wide_int &mask,
return val ^ sgnbit; 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 /* 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. the condition COND contributing to the conditional jump pointed to by BSI.
Invert the condition COND if INVERT is true. */ 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, ...@@ -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 /* Only register an ASSERT_EXPR if NAME was found in the sub-graph
reachable from E. */ reachable from E. */
if (live_on_edge (e, name)) 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 /* In the case of NAME <= CST and NAME being defined as
NAME = (unsigned) NAME2 + CST2 we can assert NAME2 >= -CST2 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, ...@@ -7538,6 +7688,39 @@ vrp_evaluate_conditional_warnv_with_ops (enum tree_code code, tree op0,
&& !POINTER_TYPE_P (TREE_TYPE (op0))) && !POINTER_TYPE_P (TREE_TYPE (op0)))
return NULL_TREE; 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 if ((ret = vrp_evaluate_conditional_warnv_with_ops_using_ranges
(code, op0, op1, strict_overflow_p))) (code, op0, op1, strict_overflow_p)))
return ret; return ret;
...@@ -8620,6 +8803,17 @@ intersect_ranges (enum value_range_type *vr0type, ...@@ -8620,6 +8803,17 @@ intersect_ranges (enum value_range_type *vr0type,
else if (vrp_val_is_min (vr1min) else if (vrp_val_is_min (vr1min)
&& vrp_val_is_max (vr1max)) && 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 choose the range. */
else else
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment