From ba55e50356632210db368c5f7a84b3e608d21b60 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Fri, 4 Feb 2022 16:50:31 -0500
Subject: [PATCH 3/3] Update non-null interface to provide better precision.

This changes the non-null interface to provide more granularity and faciltate
more precise queries using the new 2 bit system.  This enables the new
register_side_effects code to more precisely track the non-null property.

	PR tree-optimization/104288
	gcc/
	* gimple-range-cache.cc (non_null_ref::non_null_deref_p): Delete.
	(non_null_ref::always_nonnull_p): New.
	(non_null_ref::contains_nonnull_p): New.
	(non_null_ref::is_nonnull_dominated_p): New.
	(non_null_ref::adjust_range): Delete.
	(non_null_ref::maybe_adjust_range: New.
	(ranger_cache::range_of_def): Remove call to adjust_range.
	(ranger_cache::entry_range): Remove call to adjust_range.
	(ranger_cache::exit_range): Use contains_nonnull_p.
	(ranger_cache::range_of_expr): Use always_nonnull_p.
	(ranger_cache::fill_block_cache): Call contains_nonnull_p.
	(ranger_cache::range_from_dom): Call contains_nonnull_p.
	* gimple-range-cache.h (class non_null_ref): Change prototypes.
	* gimple-range-path.cc (path_range_query::range_defined_in_block): Use
	contains_nonnull_p.
	(path_range_query::adjust_for_non_null_uses): Use contains_nonnull_p.
	* gimple-range.cc (gimple_ranger::range_of_expr): Use always_nonnull_p.
	(gimple_ranger::range_on_entry): Use is_nonnull_dominated_p.
	(gimple_ranger::range_on_exit): Use contains_nonnull_p.

	testsuite/gcc/
	* gcc.dg/pr104288.c: New.
---
 gcc/gimple-range-cache.cc       | 89 +++++++++++++++++++--------------
 gcc/gimple-range-cache.h        |  7 +--
 gcc/gimple-range-path.cc        |  7 +--
 gcc/gimple-range.cc             | 16 ++++--
 gcc/testsuite/gcc.dg/pr104288.c | 23 +++++++++
 5 files changed, 94 insertions(+), 48 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr104288.c

diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc
index d14dc1284f2..72751194c25 100644
--- a/gcc/gimple-range-cache.cc
+++ b/gcc/gimple-range-cache.cc
@@ -92,64 +92,79 @@ non_null_ref::set_bb_state (tree name, basic_block bb, unsigned long val)
   bitmap_set_aligned_chunk (m_nn[v], bb->index, 2, val);
 }
 
-// Return true if NAME has a non-null dereference in block bb.  If this is the
-// first query for NAME, calculate the summary first.
-// If SEARCH_DOM is true, the search the dominator tree as well.
+// Return true if the state of NAME is always non-zero in BB.
 
 bool
-non_null_ref::non_null_deref_p (tree name, basic_block bb, bool search_dom)
+non_null_ref::always_nonnull_p (tree name, basic_block bb)
+{
+  if (POINTER_TYPE_P (TREE_TYPE (name))
+      && get_bb_state (name, bb) == NN_NON_ZERO)
+    return true;
+  return false;
+}
+
+// Return true if the state of NAME is non-zero at any point in BB.
+
+bool
+non_null_ref::contains_nonnull_p (tree name, basic_block bb)
+{
+  if (POINTER_TYPE_P (TREE_TYPE (name))
+      && get_bb_state (name, bb) >= NN_NON_ZERO)
+    return true;
+  return false;
+}
+
+// Return true if NAME has a non-null reference in a block dominated by BB.
+// If dominators are not available, simple return false.
+
+bool
+non_null_ref::is_nonnull_dominated_p (tree name, basic_block bb)
 {
-  unsigned v = SSA_NAME_VERSION (name);
   if (!POINTER_TYPE_P (TREE_TYPE (name)))
     return false;
 
-  if (get_bb_state (name, bb) >= NN_NON_ZERO)
-    return true;
+  // Ensure the tables are allocated and available.
+  get_bb_state (name, bb);
 
   // See if any dominator has set non-zero.
-  if (search_dom && dom_info_available_p (CDI_DOMINATORS))
+  if (dom_info_available_p (CDI_DOMINATORS))
     {
       // Search back to the Def block, or the top, whichever is closer.
+      // As long as it is non-zero somewhere in a pred block, its nonzero here.
+      unsigned v = SSA_NAME_VERSION (name);
       basic_block def_bb = gimple_bb (SSA_NAME_DEF_STMT (name));
+      gcc_checking_assert (bb && def_bb != bb);
       basic_block def_dom = def_bb
 			    ? get_immediate_dominator (CDI_DOMINATORS, def_bb)
 			    : NULL;
-      for ( ;
-	    bb && bb != def_dom;
-	    bb = get_immediate_dominator (CDI_DOMINATORS, bb))
+      for (bb = get_immediate_dominator (CDI_DOMINATORS, bb);
+	   bb && bb != def_dom;
+	   bb = get_immediate_dominator (CDI_DOMINATORS, bb))
 	if (bitmap_get_aligned_chunk (m_nn[v], bb->index, 2) >= NN_NON_ZERO)
 	  return true;
     }
   return false;
 }
 
-// If NAME has a non-null dereference in block BB, adjust R with the
-// non-zero information from non_null_deref_p, and return TRUE.  If
-// SEARCH_DOM is true, non_null_deref_p should search the dominator tree.
+// If the range of R is not already non-null, and we are allowing non-null
+// ranges to be adjusted, then remove 0 from the range and return TRUE.
 
 bool
-non_null_ref::adjust_range (irange &r, tree name, basic_block bb,
-			    bool search_dom)
+non_null_ref::maybe_adjust_range (irange &r)
 {
   // Non-call exceptions mean we could throw in the middle of the
   // block, so just punt on those for now.
   if (cfun->can_throw_non_call_exceptions)
     return false;
 
-  // We only care about the null / non-null property of pointers.
-  if (!POINTER_TYPE_P (TREE_TYPE (name)))
-    return false;
-  if (r.undefined_p () || r.lower_bound () != 0 || r.upper_bound () == 0)
+  if (r.undefined_p () || !POINTER_TYPE_P (r.type ())
+      || r.lower_bound () != 0 || r.upper_bound () == 0)
     return false;
-  // Check if pointers have any non-null dereferences.
-  if (non_null_deref_p (name, bb, search_dom))
-    {
-      // Remove zero from the range.
-      unsigned prec = TYPE_PRECISION (TREE_TYPE (name));
-      r.intersect (wi::one (prec), wi::max_value (prec, UNSIGNED));
-      return true;
-    }
-  return false;
+
+  // Remove zero from the range.
+  unsigned prec = TYPE_PRECISION (r.type ());
+  r.intersect (wi::one (prec), wi::max_value (prec, UNSIGNED));
+  return true;
 }
 
 // Allocate and populate the non-null state map for NAME.
@@ -1158,9 +1173,6 @@ ranger_cache::range_of_def (irange &r, tree name, basic_block bb)
       else
 	r = gimple_range_global (name);
     }
-
-  if (bb)
-    m_non_null.adjust_range (r, name, bb, false);
 }
 
 // Get the range of NAME as it occurs on entry to block BB.
@@ -1178,8 +1190,6 @@ ranger_cache::entry_range (irange &r, tree name, basic_block bb)
   // Otherwise pick up the best available global value.
   if (!m_on_entry.get_bb_range (r, name, bb))
     range_of_def (r, name);
-
-  m_non_null.adjust_range (r, name, bb, false);
 }
 
 // Get the range of NAME as it occurs on exit from block BB.
@@ -1199,6 +1209,9 @@ ranger_cache::exit_range (irange &r, tree name, basic_block bb)
     range_of_def (r, name, bb);
   else
     entry_range (r, name, bb);
+
+  if (m_non_null.contains_nonnull_p (name, bb))
+    m_non_null.maybe_adjust_range (r);
  }
 
 
@@ -1221,6 +1234,9 @@ ranger_cache::range_of_expr (irange &r, tree name, gimple *stmt)
     range_of_def (r, name, bb);
   else
     entry_range (r, name, bb);
+
+  if (m_non_null.always_nonnull_p (name, bb))
+    m_non_null.maybe_adjust_range (r);
   return true;
 }
 
@@ -1506,8 +1522,7 @@ ranger_cache::fill_block_cache (tree name, basic_block bb, basic_block def_bb)
 
 	  // Regardless of whether we have visited pred or not, if the
 	  // pred has a non-null reference, revisit this block.
-	  // Don't search the DOM tree.
-	  if (m_non_null.non_null_deref_p (name, pred, false))
+	  if (m_non_null.contains_nonnull_p (name, pred))
 	    {
 	      if (DEBUG_RANGE_CACHE)
 		fprintf (dump_file, "nonnull: update ");
@@ -1582,7 +1597,7 @@ ranger_cache::range_from_dom (irange &r, tree name, basic_block bb)
 	}
 
       // Flag if we see a non-null reference during this walk.
-      if (m_non_null.non_null_deref_p (name, bb, false))
+      if (m_non_null.contains_nonnull_p (name, bb))
 	non_null = true;
 
       // If range-on-entry is set in this block, it can be used.
diff --git a/gcc/gimple-range-cache.h b/gcc/gimple-range-cache.h
index 0d073213226..fc611487fad 100644
--- a/gcc/gimple-range-cache.h
+++ b/gcc/gimple-range-cache.h
@@ -33,9 +33,10 @@ class non_null_ref
 public:
   non_null_ref ();
   ~non_null_ref ();
-  bool non_null_deref_p (tree name, basic_block bb, bool search_dom = true);
-  bool adjust_range (irange &r, tree name, basic_block bb,
-		     bool search_dom = true);
+  bool always_nonnull_p (tree name, basic_block bb);
+  bool contains_nonnull_p (tree name, basic_block bb);
+  bool is_nonnull_dominated_p (tree name, basic_block bb);
+  bool maybe_adjust_range (irange &r);
   void block_apply_nonnull (gimple *s);
   void try_update_to_always_nonnull (gimple *s, tree name);
 private:
diff --git a/gcc/gimple-range-path.cc b/gcc/gimple-range-path.cc
index 3ee4989f4b0..74ce9357fc3 100644
--- a/gcc/gimple-range-path.cc
+++ b/gcc/gimple-range-path.cc
@@ -354,8 +354,8 @@ path_range_query::range_defined_in_block (irange &r, tree name, basic_block bb)
 	r.set_varying (TREE_TYPE (name));
     }
 
-  if (bb)
-    m_non_null.adjust_range (r, name, bb);
+  if (bb && m_non_null.contains_nonnull_p (name, bb))
+    m_non_null.maybe_adjust_range (r);
 
   if (DEBUG_SOLVER && (bb || !r.varying_p ()))
     {
@@ -525,7 +525,8 @@ path_range_query::adjust_for_non_null_uses (basic_block bb)
       else
 	r.set_varying (TREE_TYPE (name));
 
-      if (m_non_null.adjust_range (r, name, bb))
+      if (m_non_null.contains_nonnull_p (name, bb)
+	  && m_non_null.maybe_adjust_range (r))
 	set_cache (r, name);
     }
 }
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index eb599d7f0d9..12961dbc971 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -117,13 +117,13 @@ gimple_ranger::range_of_expr (irange &r, tree expr, gimple *stmt)
 
       // If name is defined in this block, try to get an range from S.
       if (def_stmt && gimple_bb (def_stmt) == bb)
-	{
-	  range_of_stmt (r, def_stmt, expr);
-	  m_cache.m_non_null.adjust_range (r, expr, bb, true);
-	}
+	range_of_stmt (r, def_stmt, expr);
       // Otherwise OP comes from outside this block, use range on entry.
       else
 	range_on_entry (r, bb, expr);
+      // If NAME is known to be non-null in this block, adjust the range.
+      if (r.varying_p () && m_cache.m_non_null.always_nonnull_p (expr, bb))
+	m_cache.m_non_null.maybe_adjust_range (r);
     }
   if (idx)
     tracer.trailer (idx, "range_of_expr", true, expr, r);
@@ -152,7 +152,9 @@ gimple_ranger::range_on_entry (irange &r, basic_block bb, tree name)
   if (m_cache.block_range (entry_range, bb, name))
     r.intersect (entry_range);
 
-  m_cache.m_non_null.adjust_range (r, name, bb, true);
+  // Pick up any nonnull info from the dominating predecessor.
+  if (r.varying_p () && m_cache.m_non_null.is_nonnull_dominated_p (name, bb))
+    m_cache.m_non_null.maybe_adjust_range (r);
 
   if (idx)
     tracer.trailer (idx, "range_on_entry", true, name, r);
@@ -186,6 +188,10 @@ gimple_ranger::range_on_exit (irange &r, basic_block bb, tree name)
     range_of_expr (r, name, s);
   else
     range_on_entry (r, bb, name);
+
+  if (r.varying_p () && m_cache.m_non_null.contains_nonnull_p (name, bb))
+    m_cache.m_non_null.maybe_adjust_range (r);
+
   gcc_checking_assert (r.undefined_p ()
 		       || range_compatible_p (r.type (), TREE_TYPE (name)));
   
diff --git a/gcc/testsuite/gcc.dg/pr104288.c b/gcc/testsuite/gcc.dg/pr104288.c
new file mode 100644
index 00000000000..95eb196f9e4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr104288.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */
+/* { dg-skip-if "" { keeps_null_pointer_checks } } */
+
+void keep(int result) __attribute__((noipa));
+void keep(int result)
+{
+    if (result)
+        __builtin_exit(0);
+}
+
+void foo (void *p) __attribute__((nonnull(1)));
+
+void bar (void *p)
+{
+  keep (p == 0);
+  foo (p);
+  if (!p)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-not "abort" "evrp" } } */
+/* { dg-final { scan-tree-dump-times  "== 0B;" 1 "evrp" } } */
-- 
2.17.2

