Currently a load from a constant table gets the range of the whole
table, no matter what is known of its index.  For example, with

  static const int tbl[4] = { 0, 5, 6, 7 };

and i in [0, 1], tbl[i] is currently [0, 0][5, 7].  With this patch we
get [0, 0][5, 5].

This work comes from my DOM parity obsession, but goes beyond what DOM
gets here.  DOM folds a load from a constant table only when the index
can be determined to be a constant, as on the path through i = 1 in:

  if (c)
    i = 1;
  if (tbl[i] == 5)

With this patch we also narrow a load whose index is in a range:

  if (i < 2 && tbl[i] > 5)

tbl[i] is [0, 0][5, 5], and we can fold the comparison away.

This implements the following TODO in the code:

      /* TODO: If the array index in the expr is an SSA_NAME with a known
         range, we could use just values loaded from the corresponding array
         elements.  */

Oh, and we no longer have to special case INTEGER_CSTs, as ranger
happily handles those.

I've also added a testcase for r17-4029-g7dab38c9d7104e, a phiopt
tweak which couldn't be properly tested until now.

Tested on ppc64le Linux.

Pushed.

gcc/ChangeLog:

        * gimple-range-fold.cc (intersects_p): New.
        (class ctor_ref_range): Add SRC.
        (ctor_ref_range::accumulate_range_from_array_ref): Load only from
        the elements the range of the index reaches, a constant index
        included.
        (fold_using_range::fold_stmt): Pass the source to
        range_from_readonly_var.
        (fold_using_range::range_from_readonly_var): Add SRC.
        * gimple-range-fold.h (fold_using_range::range_from_readonly_var):
        Adjust.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/phi-opt-52.c: New test.
        * gcc.dg/tree-ssa/vrp-from-cst-agg-10.c: New test.
        * gcc.dg/tree-ssa/vrp-from-cst-agg-11.c: New test.
        * gcc.dg/tree-ssa/vrp-from-cst-agg-13.c: New test.
---
 gcc/gimple-range-fold.cc                      | 61 ++++++++++---------
 gcc/gimple-range-fold.h                       |  2 +-
 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-52.c    | 38 ++++++++++++
 .../gcc.dg/tree-ssa/vrp-from-cst-agg-10.c     | 46 ++++++++++++++
 .../gcc.dg/tree-ssa/vrp-from-cst-agg-11.c     | 27 ++++++++
 .../gcc.dg/tree-ssa/vrp-from-cst-agg-13.c     | 19 ++++++
 6 files changed, 163 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-52.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-10.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-11.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-13.c

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index 554bb4e122c..9ac5a94341c 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -687,7 +687,7 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, 
fur_source &src, tree name)
     name = gimple_get_lhs (s);
 
   // Process addresses and loads from static constructors.
-  if (gimple_code (s) == GIMPLE_ASSIGN && range_from_readonly_var (r, s))
+  if (gimple_code (s) == GIMPLE_ASSIGN && range_from_readonly_var (r, s, src))
     return true;
 
   // Save the current range query and restore it before returning.
@@ -992,6 +992,16 @@ fold_using_range::range_of_address (prange &r, gimple 
*stmt, fur_source &src)
   return true;
 }
 
+// Does R contain anything in [LO, HI]?
+
+static bool
+intersects_p (const irange &r, const wide_int &lo, const wide_int &hi)
+{
+  int_range_max tmp (r);
+  tmp.intersect (int_range<1> (r.type (), lo, hi));
+  return !tmp.undefined_p ();
+}
+
 // The range of a load from a read-only aggregate: walk its initializer along
 // the component path of the reference, COMPONENT_PATH, from the aggregate out,
 // unioning into R the constants the load can reach.  For example:
@@ -1006,8 +1016,10 @@ fold_using_range::range_of_address (prange &r, gimple 
*stmt, fur_source &src)
 class ctor_ref_range
 {
 public:
-  ctor_ref_range (vrange &r, tree type, const vec<tree> &component_path)
-    : m_r (r), m_type (type), m_component_path (component_path) { }
+  ctor_ref_range (vrange &r, tree type, const vec<tree> &component_path,
+                 fur_source &src)
+    : m_r (r), m_type (type), m_component_path (component_path),
+      m_src (src) { }
   bool accumulate_range (tree init, unsigned i);
 private:
   bool accumulate_range_from_scalar (tree init);
@@ -1018,6 +1030,7 @@ private:
   vrange &m_r;
   tree m_type;
   const vec<tree> &m_component_path;
+  fur_source &m_src;
 };
 
 // Accumulate the range of the load from INIT, the initializer reached after
@@ -1132,24 +1145,6 @@ bool
 ctor_ref_range::accumulate_range_from_array_ref (tree init, unsigned i,
                                                 tree idx)
 {
-  if (TREE_CODE (idx) == INTEGER_CST)
-    {
-      unsigned ctor_idx;
-      tree val = get_array_ctor_element_at_index (init, wi::to_offset (idx),
-                                                 &ctor_idx);
-      if (!val)
-       {
-         // An index the initializer skips over, like t[1] of
-         //   static const int t[4] = { [2] = 5 };
-         // is not read as zero, unlike one past its end.
-         if (ctor_idx < CONSTRUCTOR_NELTS (init))
-           return false;
-         accumulate_range_from_missing_constructor_elt ();
-         return true;
-       }
-      return accumulate_range (val, i);
-    }
-
   tree domain = TYPE_DOMAIN (TREE_TYPE (init));
   if (!TYPE_MIN_VALUE (domain)
       || !TYPE_MAX_VALUE (domain)
@@ -1172,8 +1167,18 @@ ctor_ref_range::accumulate_range_from_array_ref (tree 
init, unsigned i,
     return true;
 
   unsigned prec = TYPE_PRECISION (type);
-  int_range_max missing (type, min, max);
-  wide_int next = missing.lower_bound ();
+  int_range<1> domain_r (type, min, max);
+
+  // Narrow IDX to the ranges it can actually take.
+  int_range_max idx_r;
+  m_src.get_operand (idx_r, idx);
+  range_cast (idx_r, type);
+  idx_r.intersect (domain_r);
+  if (idx_r.undefined_p ())
+    return false;
+
+  int_range_max missing (idx_r);
+  wide_int next = domain_r.lower_bound ();
   unsigned ix;
   tree index, val;
 
@@ -1203,10 +1208,7 @@ ctor_ref_range::accumulate_range_from_array_ref (tree 
init, unsigned i,
       missing.intersect (int_range<2> (type, lo, hi, VR_ANTI_RANGE));
       next = wi::add (hi, 1);
 
-      /* TODO: If the array index in the expr is an SSA_NAME with a known
-        range, we could use just values loaded from the corresponding array
-        elements.  */
-      if (!accumulate_range (val, i))
+      if (intersects_p (idx_r, lo, hi) && !accumulate_range (val, i))
        return false;
     }
 
@@ -1234,7 +1236,8 @@ 
ctor_ref_range::accumulate_range_from_missing_constructor_elt ()
 // false and leave R untouched.
 
 bool
-fold_using_range::range_from_readonly_var (vrange &r, gimple *stmt)
+fold_using_range::range_from_readonly_var (vrange &r, gimple *stmt,
+                                          fur_source &src)
 {
   gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
   tree type = TREE_TYPE (gimple_assign_lhs (stmt));
@@ -1279,7 +1282,7 @@ fold_using_range::range_from_readonly_var (vrange &r, 
gimple *stmt)
     return false;
 
   value_range tmp (type);
-  ctor_ref_range load (tmp, type, component_path);
+  ctor_ref_range load (tmp, type, component_path, src);
   bool res = load.accumulate_range (ctor, 0) && !tmp.varying_p ();
   if (res)
     r = tmp;
diff --git a/gcc/gimple-range-fold.h b/gcc/gimple-range-fold.h
index bf4310847e4..0cf14cf90dd 100644
--- a/gcc/gimple-range-fold.h
+++ b/gcc/gimple-range-fold.h
@@ -181,7 +181,7 @@ protected:
   bool range_of_call (vrange &r, gcall *call, fur_source &src);
   bool range_of_cond_expr (vrange &r, gassign* cond, fur_source &src);
   bool range_of_address (prange &r, gimple *s, fur_source &src);
-  bool range_from_readonly_var (vrange &r, gimple *stmt);
+  bool range_from_readonly_var (vrange &r, gimple *stmt, fur_source &src);
   bool range_of_phi (vrange &r, gphi *phi, fur_source &src);
   void range_of_ssa_name_with_loop_info (vrange &, tree, class loop *, gphi *,
                                         fur_source &src);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-52.c 
b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-52.c
new file mode 100644
index 00000000000..583b32ba72c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-52.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+/* The range of the load of sa[i].a under i < 2 is [1, 3].  Once it is
+   hoisted above the condition, that range must not fold the later
+   sa[i].a > 3.
+
+   This is the testcase for r17-4029-g7dab38c9d7104e ("phiopt: clear the
+   range info of hoisted adjacent loads"), which could not be exercised
+   until now.  */
+
+struct S { int a, b; };
+static const struct S sa[4] = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
+volatile int vol;
+
+__attribute__((noipa)) int
+f (int i)
+{
+  int r;
+  if (i < 2)
+    r = sa[i].a;
+  else
+    r = sa[i].b;
+  /* Enough statements that the merge block is not worth threading.  */
+  vol; vol; vol; vol; vol; vol; vol; vol;
+  vol; vol; vol; vol; vol; vol; vol; vol;
+  if (sa[i].a > 3)
+    return 100 + r;
+  return r;
+}
+
+int
+main ()
+{
+  if (f (0) != 1 || f (1) != 3 || f (2) != 106 || f (3) != 108)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-10.c 
b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-10.c
new file mode 100644
index 00000000000..e329f59259f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-10.c
@@ -0,0 +1,46 @@
+/* { dg-do link } */
+/* { dg-options "-O2 -fgimple" } */
+
+/* This is:
+
+     if (i < 2 && tbl[i] > 5)
+       link_error ();
+
+   The index is [0, 1] so the range for v_3 should be [0, 0][5, 5].  */
+
+static const int tbl[4] = { 0, 5, 6, 7 };
+
+void link_error (void);
+
+[[gnu::noipa]] void __GIMPLE (ssa,startwith("evrp"))
+foo (unsigned int i)
+{
+  int v;
+
+  __BB(2):
+  if (i_2(D) < 2u)
+    goto __BB3;
+  else
+    goto __BB5;
+
+  __BB(3):
+  v_3 = tbl[i_2(D)];
+  if (v_3 > 5)
+    goto __BB4;
+  else
+    goto __BB5;
+
+  __BB(4):
+  link_error ();
+  goto __BB5;
+
+  __BB(5):
+  return;
+}
+
+int
+main (void)
+{
+  foo (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-11.c 
b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-11.c
new file mode 100644
index 00000000000..425981f1f21
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-11.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -fdump-tree-evrp" } */
+
+/* The index is [0, 2] so the range for v_3 should be [0, 0][5, 5].  */
+
+static const int tbl[8] = { [2] = 5, [5] = 9 };
+
+int __GIMPLE (ssa,startwith("evrp"))
+foo (unsigned int i)
+{
+  int v;
+
+  __BB(2):
+  if (i_2(D) > 2u)
+    goto __BB4;
+  else
+    goto __BB3;
+
+  __BB(3):
+  v_3 = tbl[i_2(D)];
+  return v_3;
+
+  __BB(4):
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "v_3 = .irange. int .0, 0..5, 5.\n" "evrp" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-13.c 
b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-13.c
new file mode 100644
index 00000000000..2ead51b33ec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-13.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -fdump-tree-evrp" } */
+
+/* The index is unknown so the range for v_3 should be [0, 0][7, 7].  */
+
+static const struct { int a; int b[4]; } tbl[2]
+  = { { 1, { [3] = 5 } }, { 2, { 6, 7, 8, 9 } } };
+
+int __GIMPLE (ssa,startwith("evrp"))
+foo (int i)
+{
+  int v;
+
+  __BB(2):
+  v_3 = tbl[i_2(D)].b[1];
+  return v_3;
+}
+
+/* { dg-final { scan-tree-dump "v_3 = .irange. int .0, 0..7, 7.\n" "evrp" } } 
*/
-- 
2.53.0

Reply via email to