From 5d740f56a162702a33379789a4d6134d9733aa71 Mon Sep 17 00:00:00 2001
From: Martin Jambor <mjambor@suse.cz>
Date: Tue, 17 Dec 2024 11:17:14 +0100
Subject: [PATCH] ipa: Improve how we derive value ranges from IPA invariants

I believe that the current function ipa_range_set_and_normalize lacks
a check that a base of an ADDR_EXPR lacks a test whether the base
really cannot be NULL, so this patch adds it.  Moreover, I never liked
the name as I do not think it makes the value of ranges any more
normal but rather just special-cases non-zero ip_invariant pointers.
Therefore, I have given it a different name and moved it to a .cc
file, our LTO bootstrap should inline (and/or split) it if necessary
anyway.

Because, as Honza correctly pointed out, deriving non-NULLness from a
pointer depends on flag_delete_null_pointer_checks which is an
optimization flag and thus depends on a given function, in this
version of the patch ipa_get_range_from_ip_invariant gets a
context_node parameter for that purpose.  This then needs to be used
within symtab_node::nonzero_address which gets a special overload in
which the value of the flag can be provided as a parameter.

gcc/ChangeLog:

2024-12-11  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (symtab_node): Add a new overload of nonzero_address.
	* symtab.cc (symtab_node::nonzero_address): Add a new overload whith a
	parameter for delete_null_pointer_checks.  Make the original overload
	call the new one which has retains the actual implementation.
	* ipa-prop.h (ipa_get_range_from_ip_invariant): Declare.
	(ipa_range_set_and_normalize): Remove.
	* ipa-prop.cc (ipa_get_range_from_ip_invariant): New function.
	(ipa_range_set_and_normalize): Remove.
	* ipa-cp.cc (ipa_vr_intersect_with_arith_jfunc): Add a new parameter
	context_node. Use ipa_get_range_from_ip_invariant instead of
	ipa_range_set_and_normalize and pass to it the new parameter.
	(ipa_value_range_from_jfunc): Pass cs->caller as the context_node to
	ipa_vr_intersect_with_arith_jfunc.
	(propagate_vr_across_jump_function): Likewise.
	(ipa_get_range_from_ip_invariant): New function.
	* ipa-fnsummary.cc (evaluate_conditions_for_known_args): Use
	ipa_get_range_from_ip_invariant instead of ipa_range_set_and_normalize
---
 gcc/cgraph.h         |  4 ++++
 gcc/ipa-cp.cc        | 12 ++++++++----
 gcc/ipa-fnsummary.cc |  4 ++--
 gcc/ipa-prop.cc      | 37 +++++++++++++++++++++++++++++++++++++
 gcc/ipa-prop.h       | 15 +--------------
 gcc/symtab.cc        | 21 +++++++++++++++------
 6 files changed, 67 insertions(+), 26 deletions(-)

diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 50bae96de4cf..9b4cb6383afc 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -431,6 +431,10 @@ public:
   /* Return true if ONE and TWO are part of the same COMDAT group.  */
   inline bool in_same_comdat_group_p (symtab_node *target);
 
+  /* Return true if symbol is known to be nonzero, assume that
+     flag_delete_null_pointer_checks is equal to delete_null_pointer_checks.  */
+  bool nonzero_address (bool delete_null_pointer_checks);
+
   /* Return true if symbol is known to be nonzero.  */
   bool nonzero_address ();
 
diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
index a664bc03f62a..5d7b3d25df5d 100644
--- a/gcc/ipa-cp.cc
+++ b/gcc/ipa-cp.cc
@@ -1693,11 +1693,14 @@ ipa_vr_operation_and_type_effects (vrange &dst_vr,
 
 /* Given a PASS_THROUGH jump function JFUNC that takes as its source SRC_VR of
    SRC_TYPE and the result needs to be DST_TYPE, if any value range information
-   can be deduced at all, intersect VR with it.  */
+   can be deduced at all, intersect VR with it.  CONTEXT_NODE is the call graph
+   node representing the function for which optimization flags should be
+   evaluated.  */
 
 static void
 ipa_vr_intersect_with_arith_jfunc (vrange &vr,
 				   ipa_jump_func *jfunc,
+				   cgraph_node *context_node,
 				   const value_range &src_vr,
 				   tree src_type,
 				   tree dst_type)
@@ -1720,7 +1723,7 @@ ipa_vr_intersect_with_arith_jfunc (vrange &vr,
   if (!handler)
     return;
   value_range op_vr (TREE_TYPE (operand));
-  ipa_range_set_and_normalize (op_vr, operand);
+  ipa_get_range_from_ip_invariant (op_vr, operand, context_node);
 
   tree operation_type;
   if (TREE_CODE_CLASS (operation) == tcc_comparison)
@@ -1776,7 +1779,8 @@ ipa_value_range_from_jfunc (vrange &vr,
       value_range srcvr;
       (*sum->m_vr)[idx].get_vrange (srcvr);
 
-      ipa_vr_intersect_with_arith_jfunc (vr, jfunc, srcvr, src_type, parm_type);
+      ipa_vr_intersect_with_arith_jfunc (vr, jfunc, cs->caller, srcvr, src_type,
+					 parm_type);
     }
 }
 
@@ -2562,7 +2566,7 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc,
 
       if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR
 	  || !ipa_edge_within_scc (cs))
-	ipa_vr_intersect_with_arith_jfunc (vr, jfunc,
+	ipa_vr_intersect_with_arith_jfunc (vr, jfunc, cs->caller,
 					   src_lats->m_value_range.m_vr,
 					   operand_type, param_type);
     }
diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc
index 3f5e09960ef8..6799ffe0b9af 100644
--- a/gcc/ipa-fnsummary.cc
+++ b/gcc/ipa-fnsummary.cc
@@ -518,7 +518,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
 		      value_range op0 (TREE_TYPE (op->val[0]));
 		      range_op_handler handler (op->code);
 
-		      ipa_range_set_and_normalize (op0, op->val[0]);
+		      ipa_get_range_from_ip_invariant (op0, op->val[0], node);
 
 		      if (!handler
 			  || !res.supports_type_p (op->type)
@@ -537,7 +537,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
 		  value_range val_vr (TREE_TYPE (c->val));
 		  range_op_handler handler (c->code);
 
-		  ipa_range_set_and_normalize (val_vr, c->val);
+		  ipa_get_range_from_ip_invariant (val_vr, c->val, node);
 
 		  if (!handler
 		      || !val_vr.supports_type_p (TREE_TYPE (c->val))
diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
index f0b915ba2be1..0daa950985b7 100644
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
@@ -2312,6 +2312,43 @@ ipa_set_jfunc_vr (ipa_jump_func *jf, const ipa_vr &vr)
   ipa_set_jfunc_vr (jf, tmp);
 }
 
+/* Given VAL that conforms to is_gimple_ip_invariant, produce a VRANGE that
+   represents it as a range.  CONTEXT_NODE is the call graph node representing
+   the function for which optimization flags should be evaluated.  */
+
+void
+ipa_get_range_from_ip_invariant (vrange &r, tree val, cgraph_node *context_node)
+{
+  if (TREE_CODE (val) == ADDR_EXPR)
+    {
+      symtab_node *symbol;
+      tree base = TREE_OPERAND (val, 0);
+      if (!DECL_P (base))
+	{
+	  r.set_varying (TREE_TYPE (val));
+	  return;
+	}
+      if (!decl_in_symtab_p (base))
+	{
+	  r.set_nonzero (TREE_TYPE (val));
+	  return;
+	}
+      if (!(symbol = symtab_node::get (base)))
+	{
+	  r.set_varying (TREE_TYPE (val));
+	  return;
+	}
+
+      bool delete_null_pointer_checks
+	= opt_for_fn (context_node->decl, flag_delete_null_pointer_checks);
+      if (symbol->nonzero_address (delete_null_pointer_checks))
+	r.set_nonzero (TREE_TYPE (val));
+      else
+	r.set_varying (TREE_TYPE (val));
+    }
+  else
+    r.set (val, val);
+}
 
 /* If T is an SSA_NAME that is the result of a simple type conversion statement
    from an integer type to another integer type which is known to be able to
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 1ff71aba8304..d77340bfb6e7 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -1258,7 +1258,7 @@ tree ipcp_get_aggregate_const (struct function *func, tree parm, bool by_ref,
 			       HOST_WIDE_INT bit_size);
 bool unadjusted_ptr_and_unit_offset (tree op, tree *ret,
 				     poly_int64 *offset_ret);
-
+void ipa_get_range_from_ip_invariant (vrange &r, tree val, cgraph_node *node);
 void ipa_prop_cc_finalize (void);
 
 /* From tree-sra.cc:  */
@@ -1267,19 +1267,6 @@ tree build_ref_for_offset (location_t, tree, poly_int64, bool, tree,
 
 /* In ipa-cp.cc  */
 void ipa_cp_cc_finalize (void);
-
-/* Set R to the range of [VAL, VAL] while normalizing addresses to
-   non-zero.  */
-
-inline void
-ipa_range_set_and_normalize (vrange &r, tree val)
-{
-  if (TREE_CODE (val) == ADDR_EXPR)
-    r.set_nonzero (TREE_TYPE (val));
-  else
-    r.set (val, val);
-}
-
 bool ipa_return_value_range (value_range &range, tree decl);
 void ipa_record_return_value_range (value_range val);
 bool ipa_jump_functions_equivalent_p (ipa_jump_func *jf1, ipa_jump_func *jf2);
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 5be463ff7682..3804383a4e1c 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -2207,10 +2207,11 @@ symtab_node::get_partitioning_class (void)
   return SYMBOL_PARTITION;
 }
 
-/* Return true when symbol is known to be non-zero.  */
+/* Return true when symbol is known to be non-zero, assume that
+   flag_delete_null_pointer_checks is equal to delete_null_pointer_checks.  */
 
 bool
-symtab_node::nonzero_address ()
+symtab_node::nonzero_address (bool delete_null_pointer_checks)
 {
   /* Weakrefs may be NULL when their target is not defined.  */
   if (alias && weakref)
@@ -2234,7 +2235,7 @@ symtab_node::nonzero_address ()
 	  if (target->resolution != LDPR_UNKNOWN
 	      && target->resolution != LDPR_UNDEF
 	      && !target->can_be_discarded_p ()
-	      && flag_delete_null_pointer_checks)
+	      && delete_null_pointer_checks)
 	    return true;
 	  return false;
 	}
@@ -2251,7 +2252,7 @@ symtab_node::nonzero_address ()
 
      When parsing, beware the cases when WEAK attribute is added later.  */
   if ((!DECL_WEAK (decl) || DECL_COMDAT (decl))
-      && flag_delete_null_pointer_checks)
+      && delete_null_pointer_checks)
     {
       refuse_visibility_changes = true;
       return true;
@@ -2262,7 +2263,7 @@ symtab_node::nonzero_address ()
      Play safe for flag_delete_null_pointer_checks where weak definition may
      be re-defined by NULL.  */
   if (definition && !DECL_EXTERNAL (decl)
-      && (flag_delete_null_pointer_checks || !DECL_WEAK (decl)))
+      && (delete_null_pointer_checks || !DECL_WEAK (decl)))
     {
       if (!DECL_WEAK (decl))
         refuse_visibility_changes = true;
@@ -2273,11 +2274,19 @@ symtab_node::nonzero_address ()
   if (resolution != LDPR_UNKNOWN
       && resolution != LDPR_UNDEF
       && !can_be_discarded_p ()
-      && flag_delete_null_pointer_checks)
+      && delete_null_pointer_checks)
     return true;
   return false;
 }
 
+/* Return true when symbol is known to be non-zero.  */
+
+bool
+symtab_node::nonzero_address ()
+{
+  return nonzero_address (flag_delete_null_pointer_checks);
+}
+
 /* Return 0 if symbol is known to have different address than S2,
    Return 1 if symbol is known to have same address as S2,
    return -1 otherwise.
-- 
GitLab