[gcc r11-4864] Normalize VARYING for -fstrict-enums.

Aldy Hernandez aldyh@gcc.gnu.org
Tue Nov 10 08:29:07 GMT 2020


https://gcc.gnu.org/g:3d3470e239e8040f642a8852721b4962b4ed36f2

commit r11-4864-g3d3470e239e8040f642a8852721b4962b4ed36f2
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Mon Nov 9 20:35:25 2020 +0100

    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.

Diff:
---
 gcc/testsuite/g++.dg/opt/pr97767.C | 10 +++++++++
 gcc/value-range.cc                 | 42 ++++++++++++++++++++++++++++++++++++--
 gcc/value-range.h                  | 23 +++++++++------------
 3 files changed, 60 insertions(+), 15 deletions(-)

diff --git a/gcc/testsuite/g++.dg/opt/pr97767.C b/gcc/testsuite/g++.dg/opt/pr97767.C
new file mode 100644
index 00000000000..da0879d0567
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/pr97767.C
@@ -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);
+}
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 61f7da278d6..f83a824a982 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -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 ();
 }
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index a483fc802dd..7428c91ea57 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -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 ()


More information about the Gcc-cvs mailing list