Normalize VARYING for -fstrict-enums.
The problem here is that the representation for VARYING in -fstrict-enums is different between value_range and irange. The helper function irange::normalize_min_max() will normalize to VARYING only if setting the range to the entire domain of the underlying type. That is, [0, 0xff..ff], not the domain as defined by -fstrict-enums. This causes problems because the multi-range version of varying_p() will return true if the range is the domain as defined by -fstrict-enums. Thus, normalize_min_max and varying_p have different concepts of varying for multi-ranges. (BTW, legacy ranges are different because they never look at the extremes of a range to determine varying-ness. They only look at the kind field.) One approach is to change all the code to limit ranges to the domain in the -fstrict-enums world, but this won't work because there are various instances of gimple where the values assigned or compared are beyond the limits of TYPE_{MIN,MAX}_VALUE. One example is the addition of 0xffffffff to represent subtraction. This patch fixes multi-range varying_p() and set_varying() to agree with the normalization code, using the extremes of the underlying type, to represent varying. gcc/ChangeLog: PR tree-optimization/97767 * value-range.cc (dump_bound_with_infinite_markers): Use wi::min_value and wi::max_value. (range_tests_strict_enum): New. (range_tests): Call range_tests_strict_enum. * value-range.h (irange::varying_p): Use wi::min_value and wi::max_value. (irange::set_varying): Same. (irange::normalize_min_max): Remove comment. gcc/testsuite/ChangeLog: * g++.dg/opt/pr97767.C: New test.
This commit is contained in:
parent
fb54c3032b
commit
3d3470e239
10
gcc/testsuite/g++.dg/opt/pr97767.C
Normal file
10
gcc/testsuite/g++.dg/opt/pr97767.C
Normal file
@ -0,0 +1,10 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-O -fstrict-enums -ftree-vrp -w" }
|
||||
|
||||
enum { E0 = 0, E1 = 1, E2 = 2 } e;
|
||||
|
||||
int
|
||||
foo (void)
|
||||
{
|
||||
return __builtin_popcount ((int) e);
|
||||
}
|
@ -1866,12 +1866,17 @@ static void
|
||||
dump_bound_with_infinite_markers (FILE *file, tree bound)
|
||||
{
|
||||
tree type = TREE_TYPE (bound);
|
||||
wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
|
||||
wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
|
||||
|
||||
if (INTEGRAL_TYPE_P (type)
|
||||
&& !TYPE_UNSIGNED (type)
|
||||
&& vrp_val_is_min (bound)
|
||||
&& TREE_CODE (bound) == INTEGER_CST
|
||||
&& wi::to_wide (bound) == type_min
|
||||
&& TYPE_PRECISION (type) != 1)
|
||||
fprintf (file, "-INF");
|
||||
else if (vrp_val_is_max (bound)
|
||||
else if (TREE_CODE (bound) == INTEGER_CST
|
||||
&& wi::to_wide (bound) == type_max
|
||||
&& TYPE_PRECISION (type) != 1)
|
||||
fprintf (file, "+INF");
|
||||
else
|
||||
@ -2241,6 +2246,38 @@ range_tests_legacy ()
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate -fstrict-enums where the domain of a type is less than the
|
||||
// underlying type.
|
||||
|
||||
static void
|
||||
range_tests_strict_enum ()
|
||||
{
|
||||
// The enum can only hold [0, 3].
|
||||
tree rtype = copy_node (unsigned_type_node);
|
||||
TYPE_MIN_VALUE (rtype) = build_int_cstu (rtype, 0);
|
||||
TYPE_MAX_VALUE (rtype) = build_int_cstu (rtype, 3);
|
||||
|
||||
// Test that even though vr1 covers the strict enum domain ([0, 3]),
|
||||
// it does not cover the domain of the underlying type.
|
||||
int_range<1> vr1 (build_int_cstu (rtype, 0), build_int_cstu (rtype, 1));
|
||||
int_range<1> vr2 (build_int_cstu (rtype, 2), build_int_cstu (rtype, 3));
|
||||
vr1.union_ (vr2);
|
||||
ASSERT_TRUE (vr1 == int_range<1> (build_int_cstu (rtype, 0),
|
||||
build_int_cstu (rtype, 3)));
|
||||
ASSERT_FALSE (vr1.varying_p ());
|
||||
|
||||
// Test that copying to a multi-range does not change things.
|
||||
int_range<2> ir1 (vr1);
|
||||
ASSERT_TRUE (ir1 == vr1);
|
||||
ASSERT_FALSE (ir1.varying_p ());
|
||||
|
||||
// The same test as above, but using TYPE_{MIN,MAX}_VALUE instead of [0,3].
|
||||
vr1 = int_range<1> (TYPE_MIN_VALUE (rtype), TYPE_MAX_VALUE (rtype));
|
||||
ir1 = vr1;
|
||||
ASSERT_TRUE (ir1 == vr1);
|
||||
ASSERT_FALSE (ir1.varying_p ());
|
||||
}
|
||||
|
||||
static void
|
||||
range_tests_misc ()
|
||||
{
|
||||
@ -2442,6 +2479,7 @@ range_tests ()
|
||||
range_tests_legacy ();
|
||||
range_tests_irange3 ();
|
||||
range_tests_int_range_max ();
|
||||
range_tests_strict_enum ();
|
||||
range_tests_misc ();
|
||||
}
|
||||
|
||||
|
@ -280,12 +280,14 @@ irange::varying_p () const
|
||||
tree l = m_base[0];
|
||||
tree u = m_base[1];
|
||||
tree t = TREE_TYPE (l);
|
||||
unsigned prec = TYPE_PRECISION (t);
|
||||
signop sign = TYPE_SIGN (t);
|
||||
if (INTEGRAL_TYPE_P (t))
|
||||
return l == TYPE_MIN_VALUE (t) && u == TYPE_MAX_VALUE (t);
|
||||
return (wi::to_wide (l) == wi::min_value (prec, sign)
|
||||
&& wi::to_wide (u) == wi::max_value (prec, sign));
|
||||
if (POINTER_TYPE_P (t))
|
||||
return wi::to_wide (l) == 0
|
||||
&& wi::to_wide (u) == wi::max_value (TYPE_PRECISION (t),
|
||||
TYPE_SIGN (t));
|
||||
return (wi::to_wide (l) == 0
|
||||
&& wi::to_wide (u) == wi::max_value (prec, sign));
|
||||
return true;
|
||||
|
||||
}
|
||||
@ -469,8 +471,10 @@ irange::set_varying (tree type)
|
||||
m_num_ranges = 1;
|
||||
if (INTEGRAL_TYPE_P (type))
|
||||
{
|
||||
m_base[0] = TYPE_MIN_VALUE (type);
|
||||
m_base[1] = TYPE_MAX_VALUE (type);
|
||||
wide_int min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
|
||||
wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
|
||||
m_base[0] = wide_int_to_tree (type, min);
|
||||
m_base[1] = wide_int_to_tree (type, max);
|
||||
}
|
||||
else if (POINTER_TYPE_P (type))
|
||||
{
|
||||
@ -566,13 +570,6 @@ irange::set_zero (tree type)
|
||||
}
|
||||
|
||||
// Normalize a range to VARYING or UNDEFINED if possible.
|
||||
//
|
||||
// Avoid using TYPE_{MIN,MAX}_VALUE because -fstrict-enums can
|
||||
// restrict those to a subset of what actually fits in the type.
|
||||
// Instead use the extremes of the type precision which will allow
|
||||
// compare_range_with_value() to check if a value is inside a range,
|
||||
// whereas if we used TYPE_*_VAL, said function would just punt upon
|
||||
// seeing a VARYING.
|
||||
|
||||
inline void
|
||||
irange::normalize_min_max ()
|
||||
|
Loading…
x
Reference in New Issue
Block a user