PR tree-optimization/83312 reports a false positive from -Warray-bounds.
The root cause is that VRP both:

(a) updates a GIMPLE_COND to be always false, and

(b) updates an ARRAY_REF in the now-unreachable other path to use an
    ASSERT_EXPR with a negative index:
      def_stmt j_6 = ASSERT_EXPR <j_9, j_9 < 0>;

When vrp_prop::check_all_array_refs () runs, the CFG hasn't yet been
updated to take account of (a), and so a false positive is emitted
when (b) is encountered.

This patch fixes the false warning by converting
  vrp_prop::check_all_array_refs
from a simple iteration over all BBs to use a new dom_walker subclass,
using the "skip_unreachable_blocks = true" mechanism to avoid analyzing
(b).

There didn't seem to be a pre-existing way to determine the unique
out-edge after a GIMPLE_COND (if it has a constant cond), so I added
a new gimple_cond_get_unique_successor_edge function.  Similarly,
something similar may apply for switches, so I put in a
gimple_get_unique_successor_edge (though I wasn't able to create a
reproducer that used a switch).

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
        PR tree-optimization/83312
        * domwalk.h (dom_walker::dom_walker): Fix typo in comment.
        * gimple.c: Include "tree-cfg.h".
        (gimple_get_unique_successor_edge): New function.
        (gimple_cond_get_unique_successor_edge): New function.
        * gimple.h (gimple_get_unique_successor_edge): New decl.
        (gimple_cond_get_unique_successor_edge): New decl.
        * tree-vrp.c (class check_array_bounds_dom_walker): New subclass
        of dom_walker.
        (vrp_prop::check_all_array_refs): Reimplement as...
        (check_array_bounds_dom_walker::before_dom_children): ...this new
        vfunc.  Replace linear search through BB block list, excluding
        those with non-executable in-edges, with dominator walk.

gcc/testsuite/ChangeLog:
        PR tree-optimization/83312
        * gcc.dg/pr83312.c: New test case.
---
 gcc/domwalk.h                  |  2 +-
 gcc/gimple.c                   | 36 +++++++++++++++++++
 gcc/gimple.h                   |  2 ++
 gcc/testsuite/gcc.dg/pr83312.c | 30 ++++++++++++++++
 gcc/tree-vrp.c                 | 80 +++++++++++++++++++++++++++---------------
 5 files changed, 120 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr83312.c

diff --git a/gcc/domwalk.h b/gcc/domwalk.h
index 6ac93eb..c7e3450 100644
--- a/gcc/domwalk.h
+++ b/gcc/domwalk.h
@@ -32,7 +32,7 @@ class dom_walker
 public:
   static const edge STOP;
 
-  /* Use SKIP_UNREACHBLE_BLOCKS = true when your client can discover
+  /* Use SKIP_UNREACHABLE_BLOCKS = true when your client can discover
      that some edges are not executable.
 
      If a client can discover that a COND, SWITCH or GOTO has a static
diff --git a/gcc/gimple.c b/gcc/gimple.c
index c986a73..e22fcda 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "tree-cfg.h"
 
 
 /* All the tuples have their operand vector (if present) at the very bottom
@@ -3087,6 +3088,41 @@ gimple_inexpensive_call_p (gcall *stmt)
   return false;
 }
 
+/* If STMT terminates its basic block, determine if it has a uniquely
+   valid successor edge and if so return it.
+
+   Otherwise, return NULL.  */
+
+edge
+gimple_get_unique_successor_edge (const gimple *stmt)
+{
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_COND:
+      return gimple_cond_get_unique_successor_edge
+       (as_a <const gcond *> (stmt));
+    default:
+      return NULL;
+    }
+}
+
+/* Determine if COND has a uniquely valid successor edge and if so return it.
+
+   Otherwise, return NULL.  */
+
+edge
+gimple_cond_get_unique_successor_edge (const gcond *cond)
+{
+  edge te, fe;
+  extract_true_false_edges_from_block (gimple_bb (cond), &te, &fe);
+  if (gimple_cond_true_p (cond))
+    return te;
+  else if (gimple_cond_false_p (cond))
+    return fe;
+  else
+    return NULL;
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 0fcdd05..ab4cb8b 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1529,6 +1529,8 @@ extern void gimple_seq_discard (gimple_seq);
 extern void maybe_remove_unused_call_args (struct function *, gimple *);
 extern bool gimple_inexpensive_call_p (gcall *);
 extern bool stmt_can_terminate_bb_p (gimple *);
+extern edge gimple_get_unique_successor_edge (const gimple *stmt);
+extern edge gimple_cond_get_unique_successor_edge (const gcond *cond);
 
 /* Formal (expression) temporary table handling: multiple occurrences of
    the same scalar expression are evaluated into the same temporary.  */
diff --git a/gcc/testsuite/gcc.dg/pr83312.c b/gcc/testsuite/gcc.dg/pr83312.c
new file mode 100644
index 0000000..2eb241d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr83312.c
@@ -0,0 +1,30 @@
+/* { dg-options "-O2 -Warray-bounds" } */
+
+struct ptlrpcd_ctl {
+  char pc_name[20];
+};
+struct ptlrpcd {
+  struct ptlrpcd_ctl pd_threads[6];
+};
+struct ptlrpcd *ptlrpcd_init_pd;
+static void ptlrpcd_ctl_init(struct ptlrpcd_ctl *pc, int index) {
+  if (index < 0)
+    __builtin_snprintf(pc->pc_name, sizeof(pc->pc_name), "ptlrpcd_rcv");
+  else
+    __builtin_snprintf(pc->pc_name, sizeof(pc->pc_name), "ptlrpcd_%d", index);
+}
+int ptlrpcd_init_ncpts;
+static int ptlrpcd_init(int nthreads) {
+  int j;
+  if (ptlrpcd_init_ncpts) {
+    ptlrpcd_ctl_init(&ptlrpcd_init_pd->pd_threads[0], -1);
+    for (j = 1; j < nthreads; j++)
+      ptlrpcd_ctl_init(&ptlrpcd_init_pd->pd_threads[j], j);
+  }
+  return 0;
+}
+int ptlrpcd_init_groupsize;
+void ptlrpcd_addref(void) {
+    ptlrpcd_init(ptlrpcd_init_groupsize);
+}
+
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index a86b382..67bc118 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -5000,44 +5000,66 @@ check_array_bounds (tree *tp, int *walk_subtree, void 
*data)
   return NULL_TREE;
 }
 
-/* Walk over all statements of all reachable BBs and call check_array_bounds
-   on them.  */
+/* A dom_walker subclass for use by vrp_prop::check_all_array_refs,
+   to walk over all statements of all reachable BBs and call
+   check_array_bounds on them.  */
 
-void
-vrp_prop::check_all_array_refs ()
+class check_array_bounds_dom_walker : public dom_walker
 {
-  basic_block bb;
-  gimple_stmt_iterator si;
+ public:
+  check_array_bounds_dom_walker (vrp_prop *prop)
+    : dom_walker (CDI_DOMINATORS, true), m_prop (prop) {}
+  ~check_array_bounds_dom_walker () {}
 
-  FOR_EACH_BB_FN (bb, cfun)
-    {
-      edge_iterator ei;
-      edge e;
-      bool executable = false;
+  edge before_dom_children (basic_block) FINAL OVERRIDE;
 
-      /* Skip blocks that were found to be unreachable.  */
-      FOR_EACH_EDGE (e, ei, bb->preds)
-       executable |= !!(e->flags & EDGE_EXECUTABLE);
-      if (!executable)
-       continue;
+ private:
+  vrp_prop *m_prop;
+};
 
-      for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
-       {
-         gimple *stmt = gsi_stmt (si);
-         struct walk_stmt_info wi;
-         if (!gimple_has_location (stmt)
-             || is_gimple_debug (stmt))
-           continue;
+/* Implementation of dom_walker::before_dom_children.
+
+   Walk over all statements of BB and call check_array_bounds on them,
+   and determine if there's a unique successor edge.  */
 
-         memset (&wi, 0, sizeof (wi));
+edge
+check_array_bounds_dom_walker::before_dom_children (basic_block bb)
+{
+  gimple_stmt_iterator si;
+  for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+    {
+      gimple *stmt = gsi_stmt (si);
+      struct walk_stmt_info wi;
+      if (!gimple_has_location (stmt)
+         || is_gimple_debug (stmt))
+       continue;
 
-         wi.info = this;
+      memset (&wi, 0, sizeof (wi));
 
-         walk_gimple_op (gsi_stmt (si),
-                         check_array_bounds,
-                         &wi);
-       }
+      wi.info = m_prop;
+
+      walk_gimple_op (stmt, check_array_bounds, &wi);
     }
+
+  /* Determine if there's a unique successor edge, and if so, return
+     that back to dom_walker, ensuring that we don't visit blocks that
+     became unreachable during the VRP propagation
+     (PR tree-optimization/83312).  */
+  gimple *last = last_stmt (bb);
+  if (last)
+    return gimple_get_unique_successor_edge (last);
+
+  return NULL;
+}
+
+/* Walk over all statements of all reachable BBs and call check_array_bounds
+   on them.  */
+
+void
+vrp_prop::check_all_array_refs ()
+{
+  check_array_bounds_dom_walker w (this);
+  w.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun));
 }
 
 /* Return true if all imm uses of VAR are either in STMT, or
-- 
1.8.5.3

Reply via email to