diff mbox series

Propagate value ranges of return values

Message ID ZVljEevbGcssSuh8@kam.mff.cuni.cz
State New
Headers show
Series Propagate value ranges of return values | expand

Commit Message

Jan Hubicka Nov. 19, 2023, 1:21 a.m. UTC
Hi,
this patch implements very basic propaation of return value ranges from VRP
pass.  This helps std::vector's push_back since we work out value range of
allocated block.  This propagates only within single translation unit.  I hoped
we will also do the propagation at WPA stage, but that needs more work on
ipa-cp side.

I also added code auto-detecting return_nonnull and corresponding -Wsuggest-attribute

Variant of this patch bootstrapped/regtested x86_64-linux, testing with
this version is running.  I plan to commit the patch at Monday provided
there are no issues.

gcc/ChangeLog:

	* cgraph.cc (add_detected_attribute_1): New function.
	(cgraph_node::add_detected_attribute): New member function.
	* cgraph.h (struct cgraph_node): Declare it.
	* common.opt: Add Wsuggest-attribute=returns_nonnull.
	* doc/invoke.texi: Document +Wsuggest-attribute=returns_nonnull.
	* gimple-range-fold.cc: Include ipa-prop and dependencies.
	(fold_using_range::range_of_call): Look for return value range.
	* ipa-prop.cc (struct ipa_return_value_summary): New structure.
	(class ipa_return_value_sum_t): New summary.
	(ipa_record_return_value_range): New function.
	(ipa_return_value_range): New function.
	* ipa-prop.h (ipa_return_value_range): Declare.
	(ipa_record_return_value_range): Declare.
	* ipa-pure-const.cc (warn_function_returns_nonnull): New function.
	* ipa-utils.h (warn_function_returns_nonnull): Declare.
	* symbol-summary.h: Fix comment typo.
	* tree-vrp.cc (execute_ranger_vrp): Record return values.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/return-value-range-1.c: New test.

Comments

Sam James Nov. 19, 2023, 11:30 a.m. UTC | #1
Jan Hubicka <hubicka@ucw.cz> writes:

> Hi,
> this patch implements very basic propaation of return value ranges from VRP
> pass.  This helps std::vector's push_back since we work out value range of
> allocated block.  This propagates only within single translation unit.  I hoped
> we will also do the propagation at WPA stage, but that needs more work on
> ipa-cp side.
>
> I also added code auto-detecting return_nonnull and corresponding -Wsuggest-attribute
>
> Variant of this patch bootstrapped/regtested x86_64-linux, testing with
> this version is running.  I plan to commit the patch at Monday provided
> there are no issues.
>
> gcc/ChangeLog:
>
> 	* cgraph.cc (add_detected_attribute_1): New function.
> 	(cgraph_node::add_detected_attribute): New member function.
> 	* cgraph.h (struct cgraph_node): Declare it.
> 	* common.opt: Add Wsuggest-attribute=returns_nonnull.
> 	* doc/invoke.texi: Document +Wsuggest-attribute=returns_nonnull.
> 	* gimple-range-fold.cc: Include ipa-prop and dependencies.
> 	(fold_using_range::range_of_call): Look for return value range.
> 	* ipa-prop.cc (struct ipa_return_value_summary): New structure.
> 	(class ipa_return_value_sum_t): New summary.
> 	(ipa_record_return_value_range): New function.
> 	(ipa_return_value_range): New function.
> 	* ipa-prop.h (ipa_return_value_range): Declare.
> 	(ipa_record_return_value_range): Declare.
> 	* ipa-pure-const.cc (warn_function_returns_nonnull): New function.
> 	* ipa-utils.h (warn_function_returns_nonnull): Declare.
> 	* symbol-summary.h: Fix comment typo.
> 	* tree-vrp.cc (execute_ranger_vrp): Record return values.
>
> gcc/testsuite/ChangeLog:
>
> 	* gcc.dg/tree-ssa/return-value-range-1.c: New test.
>
> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> index e41e5ad3ae7..71dacf23ce1 100644
> --- a/gcc/cgraph.cc
> +++ b/gcc/cgraph.cc
> @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
>    return changed;
>  }
>  
> +/* Worker to set malloc flag.  */
> +static void
> +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
> +{
> +  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
> +    {
> +      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
> +					 NULL_TREE, DECL_ATTRIBUTES (node->decl));
> +      *changed = true;
> +    }
> +
> +  ipa_ref *ref;
> +  FOR_EACH_ALIAS (node, ref)
> +    {
> +      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +      if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +	add_detected_attribute_1 (alias, attr, changed);
> +    }
> +
> +  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
> +    if (e->caller->thunk
> +	&& (e->caller->get_availability () > AVAIL_INTERPOSABLE))
> +      add_detected_attribute_1 (e->caller, attr, changed);
> +}
> +
> +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
> +
> +bool
> +cgraph_node::add_detected_attribute (const char *attr)
> +{
> +  bool changed = false;
> +
> +  if (get_availability () > AVAIL_INTERPOSABLE)
> +    add_detected_attribute_1 (this, attr, &changed);
> +  else
> +    {
> +      ipa_ref *ref;
> +
> +      FOR_EACH_ALIAS (this, ref)
> +	{
> +	  cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +	  if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +	    add_detected_attribute_1 (alias, attr, &changed);
> +	}
> +    }
> +  return changed;
> +}
> +
>  /* Worker to set noreturng flag.  */
>  static void
>  set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index cedaaac3a45..cfdd9f693a8 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
>  
>    bool set_pure_flag (bool pure, bool looping);
>  
> +  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
> +     if any.  */
> +  bool add_detected_attribute (const char *attr);
> +
>    /* Call callback on function and aliases associated to the function.
>       When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
>       skipped. */
> diff --git a/gcc/common.opt b/gcc/common.opt
> index d21db5d4a20..0be4f02677c 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -781,6 +781,10 @@ Wsuggest-attribute=malloc
>  Common Var(warn_suggest_attribute_malloc) Warning
>  Warn about functions which might be candidates for __attribute__((malloc)).
>  
> +Wsuggest-attribute=returns_nonnull

- or _?

(If changing it, needs adjustment in rest of patch too.)

> +Common Var(warn_suggest_attribute_malloc) Warning
> +Warn about functions which might be candidates for __attribute__((malloc)).
> +

Typo: s/malloc/nonnull/?
Jan Hubicka Nov. 19, 2023, 2:31 p.m. UTC | #2
> > +Wsuggest-attribute=returns_nonnull
> 
> - or _?
> 
> (If changing it, needs adjustment in rest of patch too.)
I was thinking of this and I am not sure what is better.
Sure _ in command line option looks odd, but this is an identifier
and it is returns_nonnull and not returns-nonnull.

I am not sure we have earlier situation like that.  I can live with both
variants and would be happy to hear opinions on this.
> 
> > +Common Var(warn_suggest_attribute_malloc) Warning
> > +Warn about functions which might be candidates for __attribute__((malloc)).
> > +
> 
> Typo: s/malloc/nonnull/?
Thanks!
Honza
Jan Hubicka Nov. 19, 2023, 3:05 p.m. UTC | #3
Hi,
this is updated version which also adds testuiste compensation
I lost earlier while maintaining the patch in my testing tree.
There are quite few testcases that use constant return values to hide
something from optimizer.

Bootstrapped/regtested x86_64-linux.
gcc/ChangeLog:

	* cgraph.cc (add_detected_attribute_1): New function.
	(cgraph_node::add_detected_attribute): Likewise.
	* cgraph.h (cgraph_node::add_detected_attribute): Declare.
	* common.opt: Add -Wsuggest-attribute=returns_nonnull.
	* doc/invoke.texi: Document new flag.
	* gimple-range-fold.cc (fold_using_range::range_of_call):
	Use known reutrn value ranges.
	* ipa-prop.cc (struct ipa_return_value_summary): New type.
	(class ipa_return_value_sum_t): New type.
	(ipa_return_value_sum): New summary.
	(ipa_record_return_value_range): New function.
	(ipa_return_value_range): New function.
	* ipa-prop.h (ipa_return_value_range): Declare.
	(ipa_record_return_value_range): Declare.
	* ipa-pure-const.cc (warn_function_returns_nonnull): New funcion.
	* ipa-utils.h (warn_function_returns_nonnull): Declare.
	* symbol-summary.h: Fix comment.
	* tree-vrp.cc (execute_ranger_vrp): Record return values.

gcc/testsuite/ChangeLog:

	* g++.dg/ipa/devirt-2.C: Add noipa attribute to prevent ipa-vrp.
	* g++.dg/ipa/devirt-7.C: Disable ipa-vrp.
	* g++.dg/ipa/ipa-icf-2.C: Disable ipa-vrp.
	* g++.dg/ipa/ipa-icf-3.C: Disable ipa-vrp.
	* g++.dg/ipa/ivinline-1.C: Disable ipa-vrp.
	* g++.dg/ipa/ivinline-3.C: Disable ipa-vrp.
	* g++.dg/ipa/ivinline-5.C: Disable ipa-vrp.
	* g++.dg/ipa/ivinline-8.C: Disable ipa-vrp.
	* g++.dg/ipa/nothrow-1.C: Disable ipa-vrp.
	* g++.dg/ipa/pure-const-1.C: Disable ipa-vrp.
	* g++.dg/ipa/pure-const-2.C: Disable ipa-vrp.
	* g++.dg/lto/inline-crossmodule-1_0.C: Disable ipa-vrp.
	* gcc.c-torture/compile/pr106433.c: Add noipa attribute to prevent ipa-vrp.
	* gcc.c-torture/execute/frame-address.c: Likewise.
	* gcc.dg/ipa/fopt-info-inline-1.c: Disable ipa-vrp.
	* gcc.dg/ipa/ipa-icf-25.c: Disable ipa-vrp.
	* gcc.dg/ipa/ipa-icf-38.c: Disable ipa-vrp.
	* gcc.dg/ipa/pure-const-1.c: Disable ipa-vrp.
	* gcc.dg/ipa/remref-0.c: Add noipa attribute to prevent ipa-vrp.
	* gcc.dg/tree-prof/time-profiler-1.c: Disable ipa-vrp.
	* gcc.dg/tree-prof/time-profiler-2.c: Disable ipa-vrp.
	* gcc.dg/tree-ssa/pr110269.c: Disable ipa-vrp.
	* gcc.dg/tree-ssa/pr20701.c: Disable ipa-vrp.
	* gcc.dg/tree-ssa/vrp05.c: Disable ipa-vrp.
	* gcc.dg/tree-ssa/return-value-range-1.c: New test.

diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index e41e5ad3ae7..71dacf23ce1 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
   return changed;
 }
 
+/* Worker to set malloc flag.  */
+static void
+add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
+{
+  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
+    {
+      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
+					 NULL_TREE, DECL_ATTRIBUTES (node->decl));
+      *changed = true;
+    }
+
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (node, ref)
+    {
+      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+      if (alias->get_availability () > AVAIL_INTERPOSABLE)
+	add_detected_attribute_1 (alias, attr, changed);
+    }
+
+  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+    if (e->caller->thunk
+	&& (e->caller->get_availability () > AVAIL_INTERPOSABLE))
+      add_detected_attribute_1 (e->caller, attr, changed);
+}
+
+/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
+
+bool
+cgraph_node::add_detected_attribute (const char *attr)
+{
+  bool changed = false;
+
+  if (get_availability () > AVAIL_INTERPOSABLE)
+    add_detected_attribute_1 (this, attr, &changed);
+  else
+    {
+      ipa_ref *ref;
+
+      FOR_EACH_ALIAS (this, ref)
+	{
+	  cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+	  if (alias->get_availability () > AVAIL_INTERPOSABLE)
+	    add_detected_attribute_1 (alias, attr, &changed);
+	}
+    }
+  return changed;
+}
+
 /* Worker to set noreturng flag.  */
 static void
 set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cedaaac3a45..cfdd9f693a8 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
 
   bool set_pure_flag (bool pure, bool looping);
 
+  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
+     if any.  */
+  bool add_detected_attribute (const char *attr);
+
   /* Call callback on function and aliases associated to the function.
      When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
      skipped. */
diff --git a/gcc/common.opt b/gcc/common.opt
index d21db5d4a20..c6599c7147b 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -781,6 +781,10 @@ Wsuggest-attribute=malloc
 Common Var(warn_suggest_attribute_malloc) Warning
 Warn about functions which might be candidates for __attribute__((malloc)).
 
+Wsuggest-attribute=returns_nonnull
+Common Warning
+Warn about functions which might be candidates for __attribute__((returns_nonnull)).
+
 Wsuggest-final-types
 Common Var(warn_suggest_final_types) Warning
 Warn about C++ polymorphic types where adding final keyword would improve code quality.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1f109679c70..3c33104df0a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -8093,7 +8093,7 @@ if the array is referenced as a flexible array member.
 
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
-@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
+@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|}
 Warn for cases where adding an attribute may be beneficial. The
 attributes currently supported are listed below.
 
@@ -8113,9 +8113,11 @@ attributes currently supported are listed below.
 @itemx -Wsuggest-attribute=noreturn
 @itemx -Wmissing-noreturn
 @itemx -Wsuggest-attribute=malloc
+@itemx -Wsuggest-attribute=returns_nonnull
+@itemx -Wno-suggest-attribute=returns_nonnull
 
 Warn about functions that might be candidates for attributes
-@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
+@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler
 only warns for functions visible in other compilation units or (in the case of
 @code{pure} and @code{const}) if it cannot prove that the function returns
 normally. A function returns normally if it doesn't contain an infinite loop or
diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index 6e9530c3d7f..998b7608d78 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -44,6 +44,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-query.h"
 #include "gimple-range-op.h"
 #include "gimple-range.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "ipa-utils.h"
+#include "ipa-prop.h"
 // Construct a fur_source, and set the m_query field.
 
 fur_source::fur_source (range_query *q)
@@ -1013,6 +1018,25 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
   else
     r.set_varying (type);
 
+  tree callee = gimple_call_fndecl (call);
+  if (callee
+      && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), type))
+    {
+      Value_Range val;
+      if (ipa_return_value_range (val, callee))
+	{
+	  r.intersect (val);
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Using return value range of ");
+	      print_generic_expr (dump_file, callee, TDF_SLIM);
+	      fprintf (dump_file, ": ");
+	      val.dump (dump_file);
+	      fprintf (dump_file, "\n");
+	    }
+	}
+    }
+
   // If there is an LHS, intersect that with what is known.
   if (lhs)
     {
diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
index 7de2b788185..e77bc9c340b 100644
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
@@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x)
   return gt_ggc_mx ((ipa_vr *) x);
 }
 
+/* Analysis summery of function call return value.  */
+struct GTY(()) ipa_return_value_summary
+{
+  /* Known value range.
+     This needs to be wrapped in struccture due to specific way
+     we allocate ipa_vr. */
+  ipa_vr *vr;
+};
+
+/* Function summary for return values.  */
+class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
+{
+public:
+  ipa_return_value_sum_t (symbol_table *table, bool ggc):
+    function_summary <ipa_return_value_summary *> (table, ggc) { }
+
+  /* Hook that is called by summary when a node is duplicated.  */
+  void duplicate (cgraph_node *,
+		  cgraph_node *,
+		  ipa_return_value_summary *data,
+		  ipa_return_value_summary *data2) final override
+  {
+    *data2=*data;
+  }
+};
+
+/* Variable hoding the return value summary.  */
+static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
+
 
 /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
    with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
@@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node)
   return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
 }
 
+/* Record that current function return value range is VAL.  */
+
+void
+ipa_record_return_value_range (Value_Range val)
+{
+  cgraph_node *n = cgraph_node::get (current_function_decl);
+  if (!ipa_return_value_sum)
+    {
+      if (!ipa_vr_hash_table)
+	ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
+      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
+	      ipa_return_value_sum_t (symtab, true);
+      ipa_return_value_sum->disable_insertion_hook ();
+    }
+  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Recording return range ");
+      val.dump (dump_file);
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Return true if value range of DECL is known and if so initialize RANGE.  */
+
+bool
+ipa_return_value_range (Value_Range &range, tree decl)
+{
+  cgraph_node *n = cgraph_node::get (decl);
+  if (!n || !ipa_return_value_sum)
+    return false;
+  enum availability avail;
+  n = n->ultimate_alias_target (&avail);
+  if (avail < AVAIL_AVAILABLE)
+    return false;
+  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
+    return false;
+  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
+  if (!v)
+    return false;
+  v->vr->get_vrange (range);
+  return true;
+}
+
 
 #include "gt-ipa-prop.h"
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index fcd0e5c638f..5901c805c40 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -309,7 +309,7 @@ public:
   void get_vrange (Value_Range &) const;
   bool equal_p (const vrange &) const;
   const vrange_storage *storage () const { return m_storage; }
-  void streamer_read (lto_input_block *, data_in *);
+  void streamer_read (lto_input_block *, class data_in *);
   void streamer_write (output_block *) const;
   void dump (FILE *) const;
 
@@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
     r.set (val, val);
 }
 
+bool ipa_return_value_range (Value_Range &range, tree decl);
+void ipa_record_return_value_range (Value_Range val);
+
 #endif /* IPA_PROP_H */
diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc
index 058a7dd3019..3060ffeefcd 100644
--- a/gcc/ipa-pure-const.cc
+++ b/gcc/ipa-pure-const.cc
@@ -292,6 +292,15 @@ warn_function_cold (tree decl)
 			 true, warned_about, "cold");
 }
 
+void
+warn_function_returns_nonnull (tree decl)
+{
+  static hash_set<tree> *warned_about;
+  warned_about
+    = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl,
+			 true, warned_about, "returns_nonnull");
+}
+
 /* Check to see if the use (or definition when CHECKING_WRITE is true)
    variable T is legal in a function that is either pure or const.  */
 
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 0eefcf40d44..84728c589ea 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -105,6 +105,7 @@ tree prevailing_odr_type (tree type);
 void enable_odr_based_tbaa (tree type);
 bool odr_based_tbaa_p (const_tree type);
 void set_type_canonical_for_odr_type (tree type, tree canonical);
+void warn_function_returns_nonnull (tree);
 
 void register_odr_enum (tree type);
 
diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
index 3ed61627382..5fd49a2552e 100644
--- a/gcc/symbol-summary.h
+++ b/gcc/symbol-summary.h
@@ -71,7 +71,7 @@ public:
 	= m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this);
   }
 
-  /* Enable insertion hook invocation.  */
+  /* Disable insertion hook invocation.  */
   void disable_insertion_hook ()
   {
     if (m_symtab_insertion_hook != NULL)
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C
index 48a94e09828..1797db6c81c 100644
--- a/gcc/testsuite/g++.dg/ipa/devirt-2.C
+++ b/gcc/testsuite/g++.dg/ipa/devirt-2.C
@@ -43,7 +43,7 @@ int C::foo (int i)
   return i + 3;
 }
 
-int __attribute__ ((noinline,noclone)) get_input(void)
+int __attribute__ ((noinline,noclone,noipa)) get_input(void)
 {
   return 1;
 }
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C
index f27a264fd1e..b24b2bca5f9 100644
--- a/gcc/testsuite/g++.dg/ipa/devirt-7.C
+++ b/gcc/testsuite/g++.dg/ipa/devirt-7.C
@@ -1,7 +1,7 @@
 /* Verify that IPA-CP can do devirtualization even if the virtual call
    comes from a method that has been early-inlined into a descendant.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-cp"  } */
+/* { dg-options "-O3 -fdump-ipa-cp -fno-ipa-vrp"  } */
 /* { dg-add-options bind_pic_locally } */
 
 extern "C" void abort (void);
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
index 7f56189eebb..ae121e8a762 100644
--- a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
+/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
 
 class A
 {
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
index 5a3cca24fa2..03c10f12db2 100644
--- a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
+/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
 
 __attribute__ ((noinline))
 int zero()
diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-1.C b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
index 2d988bc6d55..ccb1870ec69 100644
--- a/gcc/testsuite/g++.dg/ipa/ivinline-1.C
+++ b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
@@ -1,7 +1,7 @@
 /* Verify that simple virtual calls are inlined even without early
    inlining.  */
 /* { dg-do run { target { nonpic || pie_enabled } } } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
 
 extern "C" void abort (void);
 
diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-3.C b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
index f756a16bae9..02e7e443fa9 100644
--- a/gcc/testsuite/g++.dg/ipa/ivinline-3.C
+++ b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
@@ -1,7 +1,7 @@
 /* Verify that simple virtual calls on an object refrence are inlined
    even without early inlining.  */
 /* { dg-do run { target { nonpic || pie_enabled } } } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
 
 extern "C" void abort (void);
 
diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-5.C b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
index 6c19907686e..cb889d1e84f 100644
--- a/gcc/testsuite/g++.dg/ipa/ivinline-5.C
+++ b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
@@ -1,7 +1,7 @@
 /* Verify that virtual call inlining does not pick a wrong method when
    there is a user defined ancestor in an object.  */
 /* { dg-do run { target { nonpic || pie_enabled } } } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
 
 extern "C" void abort (void);
 
diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-8.C b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
index bc81abfe347..f29e818e357 100644
--- a/gcc/testsuite/g++.dg/ipa/ivinline-8.C
+++ b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
@@ -1,7 +1,7 @@
 /* Verify that virtual calls are inlined (ithout early inlining) even
    when their caller is itself indirectly inlined.  */
 /* { dg-do run { target { nonpic || pie_enabled } } } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
 
 extern "C" void abort (void);
 
diff --git a/gcc/testsuite/g++.dg/ipa/nothrow-1.C b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
index b30b0215924..1f243109619 100644
--- a/gcc/testsuite/g++.dg/ipa/nothrow-1.C
+++ b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized"  } */
+/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized -fno-ipa-vrp"  } */
 int *ptr;
 static int barvar;
 
diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-1.C b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
index 61940c670e7..c18278cae11 100644
--- a/gcc/testsuite/g++.dg/ipa/pure-const-1.C
+++ b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized"  } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
 int *ptr;
 static int barvar;
 
diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-2.C b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
index 6e739de4ade..d5f18bfa9be 100644
--- a/gcc/testsuite/g++.dg/ipa/pure-const-2.C
+++ b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized"  } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
 int *ptr;
 static int barvar;
 /* We can not detect A to be const because it may be interposed by unoptimized
diff --git a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
index 0294dcc4bfb..c56360ef66e 100644
--- a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
+++ b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
@@ -1,5 +1,5 @@
 // { dg-lto-do link }
-/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details" } } */
+/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details -fno-ipa-vrp" } } */
 #include "inline-crossmodule-1.h"
 int a::key ()
 {
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr106433.c b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
index b840e5ecd93..e02ad5ffe15 100644
--- a/gcc/testsuite/gcc.c-torture/compile/pr106433.c
+++ b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
@@ -2,7 +2,7 @@
 
 int m, *p;
 
-__attribute__ ((simd)) int
+__attribute__ ((simd,noipa)) int
 bar (int x)
 {
   if (x)
diff --git a/gcc/testsuite/gcc.c-torture/execute/frame-address.c b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
index 5afa691e409..5950581054d 100644
--- a/gcc/testsuite/gcc.c-torture/execute/frame-address.c
+++ b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
@@ -1,10 +1,10 @@
 /* { dg-require-effective-target return_address } */
 void abort (void);
 
-int check_fa_work (const char *, const char *) __attribute__((noinline));
-int check_fa_mid (const char *) __attribute__((noinline));
-int check_fa (char *) __attribute__((noinline));
-int how_much (void) __attribute__((noinline));
+int check_fa_work (const char *, const char *) __attribute__((noinline,noipa));
+int check_fa_mid (const char *) __attribute__((noinline,noipa));
+int check_fa (char *) __attribute__((noinline,noipa));
+int how_much (void) __attribute__((noinline,noipa));
 
 int check_fa_work (const char *c, const char *f)
 {
diff --git a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
index 4032ad13e19..155a6829b88 100644
--- a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O3 -fopt-info-inline-optimized-missed" } */
+/* { dg-options "-O3 -fopt-info-inline-optimized-missed -fno-ipa-vrp" } */
 
 static int foo (int a)
 {
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
index fad0891283e..cbda6858890 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-icf-optimized-all"  } */
+/* { dg-options "-O2 -fdump-ipa-icf-optimized-all -fno-ipa-vrp"  } */
 
 static int zip();
 static int zap();
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
index 57c5262dd4a..a8824d040e5 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
@@ -1,6 +1,6 @@
 /* { dg-do link } */
 /* { dg-require-alias "" } */
-/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized -fno-ipa-vrp" } */
 /* { dg-require-effective-target lto } */
 /* { dg-additional-sources "ipa-icf-38a.c" }*/
 
diff --git a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
index dd58457b629..10b572781c7 100644
--- a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { nonpic || pie_enabled } } } */
-/* { dg-options "-O3 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
+/* { dg-options "-O3 -fno-ipa-vrp -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
 void abort (void);
 int error_code;
 static int val;
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-0.c b/gcc/testsuite/gcc.dg/ipa/remref-0.c
index 6073c028a98..497136e3607 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-0.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-0.c
@@ -3,7 +3,7 @@
 /* { dg-do compile } */
 /* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
 
-extern int __attribute__ ((noinline, noclone, used))
+extern int __attribute__ ((noinline, noclone, used, noipa))
 stuff (int i)
 {
   return 0;
diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
index 455f923f3f4..3f1d1e04619 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O2 -fdump-ipa-profile" } */
+/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
 
 __attribute__ ((noinline))
 int foo()
diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
index e6eaeb99810..eed0b1dd08d 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O2 -fdump-ipa-profile" } */
+/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
 
 #include <unistd.h>
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
index c68a6f91604..dd5022f3b0c 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized -fno-ipa-vrp" } */
 
 void foo(void);
 static int a = 1, c;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
index f05076cafac..3a7c03b27ff 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps" } */
+/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps -fno-ipa-vrp" } */
 
 typedef struct {
   int code;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
new file mode 100644
index 00000000000..4db52233c5d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
@@ -0,0 +1,22 @@
+/* { dg-do ling } */
+/* { dg-options "-O1 -dump-tree-evrp-details" } */
+__attribute__ ((__noinline__))
+int a(char c)
+{
+	return c;
+}
+void link_error ();
+
+void
+test(int d)
+{
+	if (a(d) > 200)
+		link_error ();
+}
+int
+main(int argc, char **argv)
+{
+	test(argc);
+	return 0;
+}
+/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
index 7f38e8d3852..f7ba16c20bb 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps" } */
+/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps -fno-ipa-vrp" } */
 
 
 inline int ten()
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index 917fa873714..82001eff20e 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -52,6 +52,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "tree-dfa.h"
 #include "tree-ssa-dce.h"
+#include "alloc-pool.h"
+#include "cgraph.h"
+#include "symbol-summary.h"
+#include "ipa-utils.h"
+#include "ipa-prop.h"
+#include "attribs.h"
 
 // This class is utilized by VRP and ranger to remove __builtin_unreachable
 // calls, and reflect any resulting global ranges.
@@ -1081,6 +1087,51 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
       array_checker.check ();
     }
 
+
+  if (Value_Range::supports_type_p (TREE_TYPE
+				     (TREE_TYPE (current_function_decl)))
+      && flag_ipa_vrp
+      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
+    {
+      edge e;
+      edge_iterator ei;
+      bool found = false;
+      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
+      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+	if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
+	  {
+	    tree retval = gimple_return_retval (ret);
+	    if (!retval)
+	      {
+		return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
+		found = true;
+		continue;
+	      }
+	    Value_Range r (TREE_TYPE (retval));
+	    if (ranger->range_of_expr (r, retval, ret)
+		&& !r.undefined_p ()
+		&& !r.varying_p ())
+	      {
+		if (!found)
+		  return_range = r;
+		else
+		  return_range.union_ (r);
+	      }
+	    else
+	      return_range.set_varying (TREE_TYPE (retval));
+	    found = true;
+	  }
+      if (found && !return_range.varying_p ())
+	{
+	  ipa_record_return_value_range (return_range);
+	  if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
+	      && return_range.nonzero_p ()
+	      && cgraph_node::get (current_function_decl)
+			->add_detected_attribute ("returns_nonnull"))
+	    warn_function_returns_nonnull (current_function_decl);
+	}
+    }
+
   phi_analysis_finalize ();
   disable_ranger (fun);
   scev_finalize ();
Andrew MacLeod Nov. 20, 2023, 2:50 p.m. UTC | #4
On 11/18/23 20:21, Jan Hubicka wrote:
> Hi,
> this patch implements very basic propaation of return value ranges from VRP
> pass.  This helps std::vector's push_back since we work out value range of
> allocated block.  This propagates only within single translation unit.  I hoped
> we will also do the propagation at WPA stage, but that needs more work on
> ipa-cp side.
>
> I also added code auto-detecting return_nonnull and corresponding -Wsuggest-attribute
>
> Variant of this patch bootstrapped/regtested x86_64-linux, testing with
> this version is running.  I plan to commit the patch at Monday provided
> there are no issues.

I see no obvious issues with the ranger/vrp changes...

My only comment is that execute_ranger_vrp is called 3 times... EVRP,  
VRP1 and VRP2.. perhaps more someday.  As long as that is OK with the 
call to warn_function_returns_nonnull().

Andrew
Jan Hubicka Nov. 20, 2023, 3:34 p.m. UTC | #5
> 
> On 11/18/23 20:21, Jan Hubicka wrote:
> > Hi,
> > this patch implements very basic propaation of return value ranges from VRP
> > pass.  This helps std::vector's push_back since we work out value range of
> > allocated block.  This propagates only within single translation unit.  I hoped
> > we will also do the propagation at WPA stage, but that needs more work on
> > ipa-cp side.
> > 
> > I also added code auto-detecting return_nonnull and corresponding -Wsuggest-attribute
> > 
> > Variant of this patch bootstrapped/regtested x86_64-linux, testing with
> > this version is running.  I plan to commit the patch at Monday provided
> > there are no issues.
> 
> I see no obvious issues with the ranger/vrp changes...
> 
> My only comment is that execute_ranger_vrp is called 3 times... EVRP,  VRP1
> and VRP2.. perhaps more someday.  As long as that is OK with the call to
> warn_function_returns_nonnull().
That should be OK.  Add the nonnull attribute and also local_pure_onst
behaves same way.

Honza
> 
> Andrew
> 
>
Martin Jambor Nov. 20, 2023, 4:28 p.m. UTC | #6
Hi,

thanks for working on this.

On Sun, Nov 19 2023, Jan Hubicka wrote:
> Hi,
> this is updated version which also adds testuiste compensation
> I lost earlier while maintaining the patch in my testing tree.
> There are quite few testcases that use constant return values to hide
> something from optimizer.
>
> Bootstrapped/regtested x86_64-linux.
> gcc/ChangeLog:
>
> 	* cgraph.cc (add_detected_attribute_1): New function.
> 	(cgraph_node::add_detected_attribute): Likewise.
> 	* cgraph.h (cgraph_node::add_detected_attribute): Declare.
> 	* common.opt: Add -Wsuggest-attribute=returns_nonnull.
> 	* doc/invoke.texi: Document new flag.
> 	* gimple-range-fold.cc (fold_using_range::range_of_call):
> 	Use known reutrn value ranges.
> 	* ipa-prop.cc (struct ipa_return_value_summary): New type.
> 	(class ipa_return_value_sum_t): New type.
> 	(ipa_return_value_sum): New summary.
> 	(ipa_record_return_value_range): New function.
> 	(ipa_return_value_range): New function.
> 	* ipa-prop.h (ipa_return_value_range): Declare.
> 	(ipa_record_return_value_range): Declare.
> 	* ipa-pure-const.cc (warn_function_returns_nonnull): New funcion.
> 	* ipa-utils.h (warn_function_returns_nonnull): Declare.
> 	* symbol-summary.h: Fix comment.
> 	* tree-vrp.cc (execute_ranger_vrp): Record return values.
>
> gcc/testsuite/ChangeLog:
>
> 	* g++.dg/ipa/devirt-2.C: Add noipa attribute to prevent ipa-vrp.
> 	* g++.dg/ipa/devirt-7.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ipa-icf-2.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ipa-icf-3.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ivinline-1.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ivinline-3.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ivinline-5.C: Disable ipa-vrp.
> 	* g++.dg/ipa/ivinline-8.C: Disable ipa-vrp.
> 	* g++.dg/ipa/nothrow-1.C: Disable ipa-vrp.
> 	* g++.dg/ipa/pure-const-1.C: Disable ipa-vrp.
> 	* g++.dg/ipa/pure-const-2.C: Disable ipa-vrp.
> 	* g++.dg/lto/inline-crossmodule-1_0.C: Disable ipa-vrp.
> 	* gcc.c-torture/compile/pr106433.c: Add noipa attribute to prevent ipa-vrp.
> 	* gcc.c-torture/execute/frame-address.c: Likewise.
> 	* gcc.dg/ipa/fopt-info-inline-1.c: Disable ipa-vrp.
> 	* gcc.dg/ipa/ipa-icf-25.c: Disable ipa-vrp.
> 	* gcc.dg/ipa/ipa-icf-38.c: Disable ipa-vrp.
> 	* gcc.dg/ipa/pure-const-1.c: Disable ipa-vrp.
> 	* gcc.dg/ipa/remref-0.c: Add noipa attribute to prevent ipa-vrp.
> 	* gcc.dg/tree-prof/time-profiler-1.c: Disable ipa-vrp.
> 	* gcc.dg/tree-prof/time-profiler-2.c: Disable ipa-vrp.
> 	* gcc.dg/tree-ssa/pr110269.c: Disable ipa-vrp.
> 	* gcc.dg/tree-ssa/pr20701.c: Disable ipa-vrp.
> 	* gcc.dg/tree-ssa/vrp05.c: Disable ipa-vrp.
> 	* gcc.dg/tree-ssa/return-value-range-1.c: New test.
>
> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> index e41e5ad3ae7..71dacf23ce1 100644
> --- a/gcc/cgraph.cc
> +++ b/gcc/cgraph.cc
> @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
>    return changed;
>  }
>  
> +/* Worker to set malloc flag.  */

I think the comment must be stale, and the name of the function also, it
does not add anything, does it?

> +static void
> +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
> +{
> +  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
> +    {
> +      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
> +					 NULL_TREE, DECL_ATTRIBUTES (node->decl));
> +      *changed = true;
> +    }
> +
> +  ipa_ref *ref;
> +  FOR_EACH_ALIAS (node, ref)
> +    {
> +      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +      if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +	add_detected_attribute_1 (alias, attr, changed);
> +    }
> +
> +  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
> +    if (e->caller->thunk
> +	&& (e->caller->get_availability () > AVAIL_INTERPOSABLE))
> +      add_detected_attribute_1 (e->caller, attr, changed);
> +}
> +
> +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */

Likewise.

> +
> +bool
> +cgraph_node::add_detected_attribute (const char *attr)
> +{
> +  bool changed = false;
> +
> +  if (get_availability () > AVAIL_INTERPOSABLE)
> +    add_detected_attribute_1 (this, attr, &changed);
> +  else
> +    {
> +      ipa_ref *ref;
> +
> +      FOR_EACH_ALIAS (this, ref)
> +	{
> +	  cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +	  if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +	    add_detected_attribute_1 (alias, attr, &changed);
> +	}
> +    }
> +  return changed;
> +}
> +
>  /* Worker to set noreturng flag.  */
>  static void
>  set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)

[...]

> diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> index 6e9530c3d7f..998b7608d78 100644
> --- a/gcc/gimple-range-fold.cc
> +++ b/gcc/gimple-range-fold.cc
> @@ -44,6 +44,11 @@ along with GCC; see the file COPYING3.  If not see
>  #include "value-query.h"
>  #include "gimple-range-op.h"
>  #include "gimple-range.h"
> +#include "cgraph.h"
> +#include "alloc-pool.h"
> +#include "symbol-summary.h"
> +#include "ipa-utils.h"
> +#include "ipa-prop.h"
>  // Construct a fur_source, and set the m_query field.
>  
>  fur_source::fur_source (range_query *q)
> @@ -1013,6 +1018,25 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
>    else
>      r.set_varying (type);
>  
> +  tree callee = gimple_call_fndecl (call);
> +  if (callee
> +      && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), type))
> +    {
> +      Value_Range val;
> +      if (ipa_return_value_range (val, callee))
> +	{
> +	  r.intersect (val);
> +	  if (dump_file && (dump_flags & TDF_DETAILS))
> +	    {
> +	      fprintf (dump_file, "Using return value range of ");
> +	      print_generic_expr (dump_file, callee, TDF_SLIM);
> +	      fprintf (dump_file, ": ");
> +	      val.dump (dump_file);
> +	      fprintf (dump_file, "\n");
> +	    }
> +	}
> +    }
> +
>    // If there is an LHS, intersect that with what is known.
>    if (lhs)
>      {
> diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
> index 7de2b788185..e77bc9c340b 100644
> --- a/gcc/ipa-prop.cc
> +++ b/gcc/ipa-prop.cc
> @@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x)
>    return gt_ggc_mx ((ipa_vr *) x);
>  }
>  
> +/* Analysis summery of function call return value.  */
> +struct GTY(()) ipa_return_value_summary
> +{
> +  /* Known value range.
> +     This needs to be wrapped in struccture due to specific way

structure.

> +     we allocate ipa_vr. */
> +  ipa_vr *vr;
> +};
> +
> +/* Function summary for return values.  */
> +class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
> +{
> +public:
> +  ipa_return_value_sum_t (symbol_table *table, bool ggc):
> +    function_summary <ipa_return_value_summary *> (table, ggc) { }
> +
> +  /* Hook that is called by summary when a node is duplicated.  */
> +  void duplicate (cgraph_node *,
> +		  cgraph_node *,
> +		  ipa_return_value_summary *data,
> +		  ipa_return_value_summary *data2) final override
> +  {
> +    *data2=*data;
> +  }
> +};
> +
> +/* Variable hoding the return value summary.  */
> +static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
> +
>  
>  /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
>     with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
> @@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node)
>    return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
>  }
>  
> +/* Record that current function return value range is VAL.  */
> +
> +void
> +ipa_record_return_value_range (Value_Range val)
> +{
> +  cgraph_node *n = cgraph_node::get (current_function_decl);
> +  if (!ipa_return_value_sum)
> +    {
> +      if (!ipa_vr_hash_table)
> +	ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
> +      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
> +	      ipa_return_value_sum_t (symtab, true);
> +      ipa_return_value_sum->disable_insertion_hook ();
> +    }
> +  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
> +  if (dump_file && (dump_flags & TDF_DETAILS))
> +    {
> +      fprintf (dump_file, "Recording return range ");
> +      val.dump (dump_file);
> +      fprintf (dump_file, "\n");
> +    }
> +}
> +
> +/* Return true if value range of DECL is known and if so initialize
> RANGE.  */

Perhaps better: "If value range of the value returned by function DECL
is known, return true and set RANGE to it."  But I guess it does not
matter much.

> +
> +bool
> +ipa_return_value_range (Value_Range &range, tree decl)
> +{
> +  cgraph_node *n = cgraph_node::get (decl);
> +  if (!n || !ipa_return_value_sum)
> +    return false;
> +  enum availability avail;
> +  n = n->ultimate_alias_target (&avail);
> +  if (avail < AVAIL_AVAILABLE)
> +    return false;
> +  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
> +    return false;
> +  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
> +  if (!v)
> +    return false;
> +  v->vr->get_vrange (range);
> +  return true;
> +}
> +
>  
>  #include "gt-ipa-prop.h"
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index fcd0e5c638f..5901c805c40 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -309,7 +309,7 @@ public:
>    void get_vrange (Value_Range &) const;
>    bool equal_p (const vrange &) const;
>    const vrange_storage *storage () const { return m_storage; }
> -  void streamer_read (lto_input_block *, data_in *);
> +  void streamer_read (lto_input_block *, class data_in *);

Is this an intended change?  Or is there some other hunk for
streamer_read missing?


>    void streamer_write (output_block *) const;
>    void dump (FILE *) const;
>  
> @@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
>      r.set (val, val);
>  }
>  
> +bool ipa_return_value_range (Value_Range &range, tree decl);
> +void ipa_record_return_value_range (Value_Range val);
> +
>  #endif /* IPA_PROP_H */

[...]

> diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
> index 917fa873714..82001eff20e 100644
> --- a/gcc/tree-vrp.cc
> +++ b/gcc/tree-vrp.cc
> @@ -52,6 +52,12 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple-fold.h"
>  #include "tree-dfa.h"
>  #include "tree-ssa-dce.h"
> +#include "alloc-pool.h"
> +#include "cgraph.h"
> +#include "symbol-summary.h"
> +#include "ipa-utils.h"
> +#include "ipa-prop.h"
> +#include "attribs.h"
>  
>  // This class is utilized by VRP and ranger to remove __builtin_unreachable
>  // calls, and reflect any resulting global ranges.
> @@ -1081,6 +1087,51 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
>        array_checker.check ();
>      }
>  
> +
> +  if (Value_Range::supports_type_p (TREE_TYPE
> +				     (TREE_TYPE (current_function_decl)))
> +      && flag_ipa_vrp
> +      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
> +    {
> +      edge e;
> +      edge_iterator ei;
> +      bool found = false;
> +      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
> +      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> +	if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
> +	  {
> +	    tree retval = gimple_return_retval (ret);
> +	    if (!retval)
> +	      {
> +		return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
> +		found = true;
> +		continue;
> +	      }
> +	    Value_Range r (TREE_TYPE (retval));
> +	    if (ranger->range_of_expr (r, retval, ret)
> +		&& !r.undefined_p ()
> +		&& !r.varying_p ())
> +	      {
> +		if (!found)
> +		  return_range = r;
> +		else
> +		  return_range.union_ (r);
> +	      }
> +	    else
> +	      return_range.set_varying (TREE_TYPE (retval));
> +	    found = true;
> +	  }
> +      if (found && !return_range.varying_p ())
> +	{
> +	  ipa_record_return_value_range (return_range);
> +	  if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
> +	      && return_range.nonzero_p ()
> +	      && cgraph_node::get (current_function_decl)
> +			->add_detected_attribute ("returns_nonnull"))
> +	    warn_function_returns_nonnull (current_function_decl);
> +	}
> +    }
> +
>    phi_analysis_finalize ();
>    disable_ranger (fun);
>    scev_finalize ();

Otherwise it looks great.

Thanks,

Martin
Christophe Lyon Nov. 21, 2023, 1:58 p.m. UTC | #7
Hi!

On Sun, 19 Nov 2023 at 16:05, Jan Hubicka <hubicka@ucw.cz> wrote:
>
> Hi,
> this is updated version which also adds testuiste compensation
> I lost earlier while maintaining the patch in my testing tree.
> There are quite few testcases that use constant return values to hide
> something from optimizer.
>
> Bootstrapped/regtested x86_64-linux.
> gcc/ChangeLog:
>
>         * cgraph.cc (add_detected_attribute_1): New function.
>         (cgraph_node::add_detected_attribute): Likewise.
>         * cgraph.h (cgraph_node::add_detected_attribute): Declare.
>         * common.opt: Add -Wsuggest-attribute=returns_nonnull.
>         * doc/invoke.texi: Document new flag.
>         * gimple-range-fold.cc (fold_using_range::range_of_call):
>         Use known reutrn value ranges.
>         * ipa-prop.cc (struct ipa_return_value_summary): New type.
>         (class ipa_return_value_sum_t): New type.
>         (ipa_return_value_sum): New summary.
>         (ipa_record_return_value_range): New function.
>         (ipa_return_value_range): New function.
>         * ipa-prop.h (ipa_return_value_range): Declare.
>         (ipa_record_return_value_range): Declare.
>         * ipa-pure-const.cc (warn_function_returns_nonnull): New funcion.
>         * ipa-utils.h (warn_function_returns_nonnull): Declare.
>         * symbol-summary.h: Fix comment.
>         * tree-vrp.cc (execute_ranger_vrp): Record return values.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/ipa/devirt-2.C: Add noipa attribute to prevent ipa-vrp.
>         * g++.dg/ipa/devirt-7.C: Disable ipa-vrp.
>         * g++.dg/ipa/ipa-icf-2.C: Disable ipa-vrp.
>         * g++.dg/ipa/ipa-icf-3.C: Disable ipa-vrp.
>         * g++.dg/ipa/ivinline-1.C: Disable ipa-vrp.
>         * g++.dg/ipa/ivinline-3.C: Disable ipa-vrp.
>         * g++.dg/ipa/ivinline-5.C: Disable ipa-vrp.
>         * g++.dg/ipa/ivinline-8.C: Disable ipa-vrp.
>         * g++.dg/ipa/nothrow-1.C: Disable ipa-vrp.
>         * g++.dg/ipa/pure-const-1.C: Disable ipa-vrp.
>         * g++.dg/ipa/pure-const-2.C: Disable ipa-vrp.
>         * g++.dg/lto/inline-crossmodule-1_0.C: Disable ipa-vrp.
>         * gcc.c-torture/compile/pr106433.c: Add noipa attribute to prevent ipa-vrp.
>         * gcc.c-torture/execute/frame-address.c: Likewise.
>         * gcc.dg/ipa/fopt-info-inline-1.c: Disable ipa-vrp.
>         * gcc.dg/ipa/ipa-icf-25.c: Disable ipa-vrp.
>         * gcc.dg/ipa/ipa-icf-38.c: Disable ipa-vrp.
>         * gcc.dg/ipa/pure-const-1.c: Disable ipa-vrp.
>         * gcc.dg/ipa/remref-0.c: Add noipa attribute to prevent ipa-vrp.
>         * gcc.dg/tree-prof/time-profiler-1.c: Disable ipa-vrp.
>         * gcc.dg/tree-prof/time-profiler-2.c: Disable ipa-vrp.
>         * gcc.dg/tree-ssa/pr110269.c: Disable ipa-vrp.
>         * gcc.dg/tree-ssa/pr20701.c: Disable ipa-vrp.
>         * gcc.dg/tree-ssa/vrp05.c: Disable ipa-vrp.
>         * gcc.dg/tree-ssa/return-value-range-1.c: New test.
>

After this patch in addition to the problem already reported about
vlda1.c and return-value-range-1.c, we have noticed these regressions
on aarch64:
Running gcc:gcc.target/aarch64/aarch64.exp ...
FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x4667, lsl 16
FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x7a3d, lsl 32

Running gcc:gcc.target/aarch64/simd/simd.exp ...
FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
fmul[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 1
FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
fmulx[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 4
FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
fmul[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 1
FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
fmulx[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 4

We have already sent you a notification for the regression on arm, but
it includes on vla-1.c and return-value-range-1.c.
The notification email contains a pointer to the page where we record
all the configurations that regress because of this patch:

https://linaro.atlassian.net/browse/GNU-1025

Can you have a look?

Thanks,

Christophe




> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> index e41e5ad3ae7..71dacf23ce1 100644
> --- a/gcc/cgraph.cc
> +++ b/gcc/cgraph.cc
> @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
>    return changed;
>  }
>
> +/* Worker to set malloc flag.  */
> +static void
> +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
> +{
> +  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
> +    {
> +      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
> +                                        NULL_TREE, DECL_ATTRIBUTES (node->decl));
> +      *changed = true;
> +    }
> +
> +  ipa_ref *ref;
> +  FOR_EACH_ALIAS (node, ref)
> +    {
> +      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +      if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +       add_detected_attribute_1 (alias, attr, changed);
> +    }
> +
> +  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
> +    if (e->caller->thunk
> +       && (e->caller->get_availability () > AVAIL_INTERPOSABLE))
> +      add_detected_attribute_1 (e->caller, attr, changed);
> +}
> +
> +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
> +
> +bool
> +cgraph_node::add_detected_attribute (const char *attr)
> +{
> +  bool changed = false;
> +
> +  if (get_availability () > AVAIL_INTERPOSABLE)
> +    add_detected_attribute_1 (this, attr, &changed);
> +  else
> +    {
> +      ipa_ref *ref;
> +
> +      FOR_EACH_ALIAS (this, ref)
> +       {
> +         cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> +         if (alias->get_availability () > AVAIL_INTERPOSABLE)
> +           add_detected_attribute_1 (alias, attr, &changed);
> +       }
> +    }
> +  return changed;
> +}
> +
>  /* Worker to set noreturng flag.  */
>  static void
>  set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index cedaaac3a45..cfdd9f693a8 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
>
>    bool set_pure_flag (bool pure, bool looping);
>
> +  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
> +     if any.  */
> +  bool add_detected_attribute (const char *attr);
> +
>    /* Call callback on function and aliases associated to the function.
>       When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
>       skipped. */
> diff --git a/gcc/common.opt b/gcc/common.opt
> index d21db5d4a20..c6599c7147b 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -781,6 +781,10 @@ Wsuggest-attribute=malloc
>  Common Var(warn_suggest_attribute_malloc) Warning
>  Warn about functions which might be candidates for __attribute__((malloc)).
>
> +Wsuggest-attribute=returns_nonnull
> +Common Warning
> +Warn about functions which might be candidates for __attribute__((returns_nonnull)).
> +
>  Wsuggest-final-types
>  Common Var(warn_suggest_final_types) Warning
>  Warn about C++ polymorphic types where adding final keyword would improve code quality.
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 1f109679c70..3c33104df0a 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -8093,7 +8093,7 @@ if the array is referenced as a flexible array member.
>
>  @opindex Wsuggest-attribute=
>  @opindex Wno-suggest-attribute=
> -@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
> +@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|}
>  Warn for cases where adding an attribute may be beneficial. The
>  attributes currently supported are listed below.
>
> @@ -8113,9 +8113,11 @@ attributes currently supported are listed below.
>  @itemx -Wsuggest-attribute=noreturn
>  @itemx -Wmissing-noreturn
>  @itemx -Wsuggest-attribute=malloc
> +@itemx -Wsuggest-attribute=returns_nonnull
> +@itemx -Wno-suggest-attribute=returns_nonnull
>
>  Warn about functions that might be candidates for attributes
> -@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
> +@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler
>  only warns for functions visible in other compilation units or (in the case of
>  @code{pure} and @code{const}) if it cannot prove that the function returns
>  normally. A function returns normally if it doesn't contain an infinite loop or
> diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> index 6e9530c3d7f..998b7608d78 100644
> --- a/gcc/gimple-range-fold.cc
> +++ b/gcc/gimple-range-fold.cc
> @@ -44,6 +44,11 @@ along with GCC; see the file COPYING3.  If not see
>  #include "value-query.h"
>  #include "gimple-range-op.h"
>  #include "gimple-range.h"
> +#include "cgraph.h"
> +#include "alloc-pool.h"
> +#include "symbol-summary.h"
> +#include "ipa-utils.h"
> +#include "ipa-prop.h"
>  // Construct a fur_source, and set the m_query field.
>
>  fur_source::fur_source (range_query *q)
> @@ -1013,6 +1018,25 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
>    else
>      r.set_varying (type);
>
> +  tree callee = gimple_call_fndecl (call);
> +  if (callee
> +      && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), type))
> +    {
> +      Value_Range val;
> +      if (ipa_return_value_range (val, callee))
> +       {
> +         r.intersect (val);
> +         if (dump_file && (dump_flags & TDF_DETAILS))
> +           {
> +             fprintf (dump_file, "Using return value range of ");
> +             print_generic_expr (dump_file, callee, TDF_SLIM);
> +             fprintf (dump_file, ": ");
> +             val.dump (dump_file);
> +             fprintf (dump_file, "\n");
> +           }
> +       }
> +    }
> +
>    // If there is an LHS, intersect that with what is known.
>    if (lhs)
>      {
> diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
> index 7de2b788185..e77bc9c340b 100644
> --- a/gcc/ipa-prop.cc
> +++ b/gcc/ipa-prop.cc
> @@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x)
>    return gt_ggc_mx ((ipa_vr *) x);
>  }
>
> +/* Analysis summery of function call return value.  */
> +struct GTY(()) ipa_return_value_summary
> +{
> +  /* Known value range.
> +     This needs to be wrapped in struccture due to specific way
> +     we allocate ipa_vr. */
> +  ipa_vr *vr;
> +};
> +
> +/* Function summary for return values.  */
> +class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
> +{
> +public:
> +  ipa_return_value_sum_t (symbol_table *table, bool ggc):
> +    function_summary <ipa_return_value_summary *> (table, ggc) { }
> +
> +  /* Hook that is called by summary when a node is duplicated.  */
> +  void duplicate (cgraph_node *,
> +                 cgraph_node *,
> +                 ipa_return_value_summary *data,
> +                 ipa_return_value_summary *data2) final override
> +  {
> +    *data2=*data;
> +  }
> +};
> +
> +/* Variable hoding the return value summary.  */
> +static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
> +
>
>  /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
>     with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
> @@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node)
>    return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
>  }
>
> +/* Record that current function return value range is VAL.  */
> +
> +void
> +ipa_record_return_value_range (Value_Range val)
> +{
> +  cgraph_node *n = cgraph_node::get (current_function_decl);
> +  if (!ipa_return_value_sum)
> +    {
> +      if (!ipa_vr_hash_table)
> +       ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
> +      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
> +             ipa_return_value_sum_t (symtab, true);
> +      ipa_return_value_sum->disable_insertion_hook ();
> +    }
> +  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
> +  if (dump_file && (dump_flags & TDF_DETAILS))
> +    {
> +      fprintf (dump_file, "Recording return range ");
> +      val.dump (dump_file);
> +      fprintf (dump_file, "\n");
> +    }
> +}
> +
> +/* Return true if value range of DECL is known and if so initialize RANGE.  */
> +
> +bool
> +ipa_return_value_range (Value_Range &range, tree decl)
> +{
> +  cgraph_node *n = cgraph_node::get (decl);
> +  if (!n || !ipa_return_value_sum)
> +    return false;
> +  enum availability avail;
> +  n = n->ultimate_alias_target (&avail);
> +  if (avail < AVAIL_AVAILABLE)
> +    return false;
> +  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
> +    return false;
> +  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
> +  if (!v)
> +    return false;
> +  v->vr->get_vrange (range);
> +  return true;
> +}
> +
>
>  #include "gt-ipa-prop.h"
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index fcd0e5c638f..5901c805c40 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -309,7 +309,7 @@ public:
>    void get_vrange (Value_Range &) const;
>    bool equal_p (const vrange &) const;
>    const vrange_storage *storage () const { return m_storage; }
> -  void streamer_read (lto_input_block *, data_in *);
> +  void streamer_read (lto_input_block *, class data_in *);
>    void streamer_write (output_block *) const;
>    void dump (FILE *) const;
>
> @@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
>      r.set (val, val);
>  }
>
> +bool ipa_return_value_range (Value_Range &range, tree decl);
> +void ipa_record_return_value_range (Value_Range val);
> +
>  #endif /* IPA_PROP_H */
> diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc
> index 058a7dd3019..3060ffeefcd 100644
> --- a/gcc/ipa-pure-const.cc
> +++ b/gcc/ipa-pure-const.cc
> @@ -292,6 +292,15 @@ warn_function_cold (tree decl)
>                          true, warned_about, "cold");
>  }
>
> +void
> +warn_function_returns_nonnull (tree decl)
> +{
> +  static hash_set<tree> *warned_about;
> +  warned_about
> +    = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl,
> +                        true, warned_about, "returns_nonnull");
> +}
> +
>  /* Check to see if the use (or definition when CHECKING_WRITE is true)
>     variable T is legal in a function that is either pure or const.  */
>
> diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
> index 0eefcf40d44..84728c589ea 100644
> --- a/gcc/ipa-utils.h
> +++ b/gcc/ipa-utils.h
> @@ -105,6 +105,7 @@ tree prevailing_odr_type (tree type);
>  void enable_odr_based_tbaa (tree type);
>  bool odr_based_tbaa_p (const_tree type);
>  void set_type_canonical_for_odr_type (tree type, tree canonical);
> +void warn_function_returns_nonnull (tree);
>
>  void register_odr_enum (tree type);
>
> diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
> index 3ed61627382..5fd49a2552e 100644
> --- a/gcc/symbol-summary.h
> +++ b/gcc/symbol-summary.h
> @@ -71,7 +71,7 @@ public:
>         = m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this);
>    }
>
> -  /* Enable insertion hook invocation.  */
> +  /* Disable insertion hook invocation.  */
>    void disable_insertion_hook ()
>    {
>      if (m_symtab_insertion_hook != NULL)
> diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> index 48a94e09828..1797db6c81c 100644
> --- a/gcc/testsuite/g++.dg/ipa/devirt-2.C
> +++ b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> @@ -43,7 +43,7 @@ int C::foo (int i)
>    return i + 3;
>  }
>
> -int __attribute__ ((noinline,noclone)) get_input(void)
> +int __attribute__ ((noinline,noclone,noipa)) get_input(void)
>  {
>    return 1;
>  }
> diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> index f27a264fd1e..b24b2bca5f9 100644
> --- a/gcc/testsuite/g++.dg/ipa/devirt-7.C
> +++ b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> @@ -1,7 +1,7 @@
>  /* Verify that IPA-CP can do devirtualization even if the virtual call
>     comes from a method that has been early-inlined into a descendant.  */
>  /* { dg-do run } */
> -/* { dg-options "-O3 -fdump-ipa-cp"  } */
> +/* { dg-options "-O3 -fdump-ipa-cp -fno-ipa-vrp"  } */
>  /* { dg-add-options bind_pic_locally } */
>
>  extern "C" void abort (void);
> diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> index 7f56189eebb..ae121e8a762 100644
> --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
>
>  class A
>  {
> diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> index 5a3cca24fa2..03c10f12db2 100644
> --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
>
>  __attribute__ ((noinline))
>  int zero()
> diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-1.C b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> index 2d988bc6d55..ccb1870ec69 100644
> --- a/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> +++ b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> @@ -1,7 +1,7 @@
>  /* Verify that simple virtual calls are inlined even without early
>     inlining.  */
>  /* { dg-do run { target { nonpic || pie_enabled } } } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
>
>  extern "C" void abort (void);
>
> diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-3.C b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> index f756a16bae9..02e7e443fa9 100644
> --- a/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> +++ b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> @@ -1,7 +1,7 @@
>  /* Verify that simple virtual calls on an object refrence are inlined
>     even without early inlining.  */
>  /* { dg-do run { target { nonpic || pie_enabled } } } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
>
>  extern "C" void abort (void);
>
> diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-5.C b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> index 6c19907686e..cb889d1e84f 100644
> --- a/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> +++ b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> @@ -1,7 +1,7 @@
>  /* Verify that virtual call inlining does not pick a wrong method when
>     there is a user defined ancestor in an object.  */
>  /* { dg-do run { target { nonpic || pie_enabled } } } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
>
>  extern "C" void abort (void);
>
> diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-8.C b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> index bc81abfe347..f29e818e357 100644
> --- a/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> +++ b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> @@ -1,7 +1,7 @@
>  /* Verify that virtual calls are inlined (ithout early inlining) even
>     when their caller is itself indirectly inlined.  */
>  /* { dg-do run { target { nonpic || pie_enabled } } } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
>
>  extern "C" void abort (void);
>
> diff --git a/gcc/testsuite/g++.dg/ipa/nothrow-1.C b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> index b30b0215924..1f243109619 100644
> --- a/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> +++ b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized"  } */
> +/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized -fno-ipa-vrp"  } */
>  int *ptr;
>  static int barvar;
>
> diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-1.C b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> index 61940c670e7..c18278cae11 100644
> --- a/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> +++ b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
>  int *ptr;
>  static int barvar;
>
> diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-2.C b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> index 6e739de4ade..d5f18bfa9be 100644
> --- a/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> +++ b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
>  int *ptr;
>  static int barvar;
>  /* We can not detect A to be const because it may be interposed by unoptimized
> diff --git a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> index 0294dcc4bfb..c56360ef66e 100644
> --- a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> +++ b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> @@ -1,5 +1,5 @@
>  // { dg-lto-do link }
> -/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details" } } */
> +/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details -fno-ipa-vrp" } } */
>  #include "inline-crossmodule-1.h"
>  int a::key ()
>  {
> diff --git a/gcc/testsuite/gcc.c-torture/compile/pr106433.c b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> index b840e5ecd93..e02ad5ffe15 100644
> --- a/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> +++ b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> @@ -2,7 +2,7 @@
>
>  int m, *p;
>
> -__attribute__ ((simd)) int
> +__attribute__ ((simd,noipa)) int
>  bar (int x)
>  {
>    if (x)
> diff --git a/gcc/testsuite/gcc.c-torture/execute/frame-address.c b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> index 5afa691e409..5950581054d 100644
> --- a/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> +++ b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> @@ -1,10 +1,10 @@
>  /* { dg-require-effective-target return_address } */
>  void abort (void);
>
> -int check_fa_work (const char *, const char *) __attribute__((noinline));
> -int check_fa_mid (const char *) __attribute__((noinline));
> -int check_fa (char *) __attribute__((noinline));
> -int how_much (void) __attribute__((noinline));
> +int check_fa_work (const char *, const char *) __attribute__((noinline,noipa));
> +int check_fa_mid (const char *) __attribute__((noinline,noipa));
> +int check_fa (char *) __attribute__((noinline,noipa));
> +int how_much (void) __attribute__((noinline,noipa));
>
>  int check_fa_work (const char *c, const char *f)
>  {
> diff --git a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> index 4032ad13e19..155a6829b88 100644
> --- a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> +++ b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> @@ -1,4 +1,4 @@
> -/* { dg-options "-O3 -fopt-info-inline-optimized-missed" } */
> +/* { dg-options "-O3 -fopt-info-inline-optimized-missed -fno-ipa-vrp" } */
>
>  static int foo (int a)
>  {
> diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> index fad0891283e..cbda6858890 100644
> --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-ipa-icf-optimized-all"  } */
> +/* { dg-options "-O2 -fdump-ipa-icf-optimized-all -fno-ipa-vrp"  } */
>
>  static int zip();
>  static int zap();
> diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> index 57c5262dd4a..a8824d040e5 100644
> --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> @@ -1,6 +1,6 @@
>  /* { dg-do link } */
>  /* { dg-require-alias "" } */
> -/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized" } */
> +/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized -fno-ipa-vrp" } */
>  /* { dg-require-effective-target lto } */
>  /* { dg-additional-sources "ipa-icf-38a.c" }*/
>
> diff --git a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> index dd58457b629..10b572781c7 100644
> --- a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> +++ b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile { target { nonpic || pie_enabled } } } */
> -/* { dg-options "-O3 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
> +/* { dg-options "-O3 -fno-ipa-vrp -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
>  void abort (void);
>  int error_code;
>  static int val;
> diff --git a/gcc/testsuite/gcc.dg/ipa/remref-0.c b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> index 6073c028a98..497136e3607 100644
> --- a/gcc/testsuite/gcc.dg/ipa/remref-0.c
> +++ b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> @@ -3,7 +3,7 @@
>  /* { dg-do compile } */
>  /* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
>
> -extern int __attribute__ ((noinline, noclone, used))
> +extern int __attribute__ ((noinline, noclone, used, noipa))
>  stuff (int i)
>  {
>    return 0;
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> index 455f923f3f4..3f1d1e04619 100644
> --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> @@ -1,4 +1,4 @@
> -/* { dg-options "-O2 -fdump-ipa-profile" } */
> +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
>
>  __attribute__ ((noinline))
>  int foo()
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> index e6eaeb99810..eed0b1dd08d 100644
> --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> @@ -1,4 +1,4 @@
> -/* { dg-options "-O2 -fdump-ipa-profile" } */
> +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
>
>  #include <unistd.h>
>
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> index c68a6f91604..dd5022f3b0c 100644
> --- a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized" } */
> +/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized -fno-ipa-vrp" } */
>
>  void foo(void);
>  static int a = 1, c;
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> index f05076cafac..3a7c03b27ff 100644
> --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps" } */
> +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps -fno-ipa-vrp" } */
>
>  typedef struct {
>    int code;
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> new file mode 100644
> index 00000000000..4db52233c5d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> @@ -0,0 +1,22 @@
> +/* { dg-do ling } */
> +/* { dg-options "-O1 -dump-tree-evrp-details" } */
> +__attribute__ ((__noinline__))
> +int a(char c)
> +{
> +       return c;
> +}
> +void link_error ();
> +
> +void
> +test(int d)
> +{
> +       if (a(d) > 200)
> +               link_error ();
> +}
> +int
> +main(int argc, char **argv)
> +{
> +       test(argc);
> +       return 0;
> +}
> +/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> index 7f38e8d3852..f7ba16c20bb 100644
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps" } */
> +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps -fno-ipa-vrp" } */
>
>
>  inline int ten()
> diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
> index 917fa873714..82001eff20e 100644
> --- a/gcc/tree-vrp.cc
> +++ b/gcc/tree-vrp.cc
> @@ -52,6 +52,12 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple-fold.h"
>  #include "tree-dfa.h"
>  #include "tree-ssa-dce.h"
> +#include "alloc-pool.h"
> +#include "cgraph.h"
> +#include "symbol-summary.h"
> +#include "ipa-utils.h"
> +#include "ipa-prop.h"
> +#include "attribs.h"
>
>  // This class is utilized by VRP and ranger to remove __builtin_unreachable
>  // calls, and reflect any resulting global ranges.
> @@ -1081,6 +1087,51 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
>        array_checker.check ();
>      }
>
> +
> +  if (Value_Range::supports_type_p (TREE_TYPE
> +                                    (TREE_TYPE (current_function_decl)))
> +      && flag_ipa_vrp
> +      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
> +    {
> +      edge e;
> +      edge_iterator ei;
> +      bool found = false;
> +      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
> +      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> +       if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
> +         {
> +           tree retval = gimple_return_retval (ret);
> +           if (!retval)
> +             {
> +               return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
> +               found = true;
> +               continue;
> +             }
> +           Value_Range r (TREE_TYPE (retval));
> +           if (ranger->range_of_expr (r, retval, ret)
> +               && !r.undefined_p ()
> +               && !r.varying_p ())
> +             {
> +               if (!found)
> +                 return_range = r;
> +               else
> +                 return_range.union_ (r);
> +             }
> +           else
> +             return_range.set_varying (TREE_TYPE (retval));
> +           found = true;
> +         }
> +      if (found && !return_range.varying_p ())
> +       {
> +         ipa_record_return_value_range (return_range);
> +         if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
> +             && return_range.nonzero_p ()
> +             && cgraph_node::get (current_function_decl)
> +                       ->add_detected_attribute ("returns_nonnull"))
> +           warn_function_returns_nonnull (current_function_decl);
> +       }
> +    }
> +
>    phi_analysis_finalize ();
>    disable_ranger (fun);
>    scev_finalize ();
Jan Hubicka Nov. 21, 2023, 2:06 p.m. UTC | #8
> After this patch in addition to the problem already reported about
> vlda1.c and return-value-range-1.c, we have noticed these regressions
> on aarch64:
> Running gcc:gcc.target/aarch64/aarch64.exp ...
> FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x4667, lsl 16
> FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x7a3d, lsl 32
> 
> Running gcc:gcc.target/aarch64/simd/simd.exp ...
> FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
> fmul[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 1
> FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
> fmulx[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 4
> FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
> fmul[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 1
> FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
> fmulx[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 4

Sorry for that - I guess we will see some on various targets.
This is quite common issue - the testcase is having
dummy_number_generator function returning constant and prevents
inlining to avoid constant being visible to compiler.  This no longer
works, since we get it from the return value range.  This should fix it.

return-value_range-1.c should be fixed now and I do not have vlda1.c in
my tree.  I will check.

diff --git a/gcc/testsuite/gcc.target/aarch64/movk.c b/gcc/testsuite/gcc.target/aarch64/movk.c
index e6e4e3a8961..6b1f3f8ecf5 100644
--- a/gcc/testsuite/gcc.target/aarch64/movk.c
+++ b/gcc/testsuite/gcc.target/aarch64/movk.c
@@ -1,8 +1,9 @@
 /* { dg-do run } */
-/* { dg-options "-O2 --save-temps -fno-inline" } */
+/* { dg-options "-O2 --save-temps" } */
 
 extern void abort (void);
 
+__attribute__ ((noipa))
 long long int
 dummy_number_generator ()
 {

> 
> We have already sent you a notification for the regression on arm, but
> it includes on vla-1.c and return-value-range-1.c.
> The notification email contains a pointer to the page where we record
> all the configurations that regress because of this patch:
> 
> https://linaro.atlassian.net/browse/GNU-1025
> 
> Can you have a look?
> 
> Thanks,
> 
> Christophe
> 
> 
> 
> 
> > diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> > index e41e5ad3ae7..71dacf23ce1 100644
> > --- a/gcc/cgraph.cc
> > +++ b/gcc/cgraph.cc
> > @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
> >    return changed;
> >  }
> >
> > +/* Worker to set malloc flag.  */
> > +static void
> > +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
> > +{
> > +  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
> > +    {
> > +      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
> > +                                        NULL_TREE, DECL_ATTRIBUTES (node->decl));
> > +      *changed = true;
> > +    }
> > +
> > +  ipa_ref *ref;
> > +  FOR_EACH_ALIAS (node, ref)
> > +    {
> > +      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> > +      if (alias->get_availability () > AVAIL_INTERPOSABLE)
> > +       add_detected_attribute_1 (alias, attr, changed);
> > +    }
> > +
> > +  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
> > +    if (e->caller->thunk
> > +       && (e->caller->get_availability () > AVAIL_INTERPOSABLE))
> > +      add_detected_attribute_1 (e->caller, attr, changed);
> > +}
> > +
> > +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
> > +
> > +bool
> > +cgraph_node::add_detected_attribute (const char *attr)
> > +{
> > +  bool changed = false;
> > +
> > +  if (get_availability () > AVAIL_INTERPOSABLE)
> > +    add_detected_attribute_1 (this, attr, &changed);
> > +  else
> > +    {
> > +      ipa_ref *ref;
> > +
> > +      FOR_EACH_ALIAS (this, ref)
> > +       {
> > +         cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> > +         if (alias->get_availability () > AVAIL_INTERPOSABLE)
> > +           add_detected_attribute_1 (alias, attr, &changed);
> > +       }
> > +    }
> > +  return changed;
> > +}
> > +
> >  /* Worker to set noreturng flag.  */
> >  static void
> >  set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
> > diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> > index cedaaac3a45..cfdd9f693a8 100644
> > --- a/gcc/cgraph.h
> > +++ b/gcc/cgraph.h
> > @@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
> >
> >    bool set_pure_flag (bool pure, bool looping);
> >
> > +  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
> > +     if any.  */
> > +  bool add_detected_attribute (const char *attr);
> > +
> >    /* Call callback on function and aliases associated to the function.
> >       When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
> >       skipped. */
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index d21db5d4a20..c6599c7147b 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -781,6 +781,10 @@ Wsuggest-attribute=malloc
> >  Common Var(warn_suggest_attribute_malloc) Warning
> >  Warn about functions which might be candidates for __attribute__((malloc)).
> >
> > +Wsuggest-attribute=returns_nonnull
> > +Common Warning
> > +Warn about functions which might be candidates for __attribute__((returns_nonnull)).
> > +
> >  Wsuggest-final-types
> >  Common Var(warn_suggest_final_types) Warning
> >  Warn about C++ polymorphic types where adding final keyword would improve code quality.
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 1f109679c70..3c33104df0a 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -8093,7 +8093,7 @@ if the array is referenced as a flexible array member.
> >
> >  @opindex Wsuggest-attribute=
> >  @opindex Wno-suggest-attribute=
> > -@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
> > +@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|}
> >  Warn for cases where adding an attribute may be beneficial. The
> >  attributes currently supported are listed below.
> >
> > @@ -8113,9 +8113,11 @@ attributes currently supported are listed below.
> >  @itemx -Wsuggest-attribute=noreturn
> >  @itemx -Wmissing-noreturn
> >  @itemx -Wsuggest-attribute=malloc
> > +@itemx -Wsuggest-attribute=returns_nonnull
> > +@itemx -Wno-suggest-attribute=returns_nonnull
> >
> >  Warn about functions that might be candidates for attributes
> > -@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
> > +@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler
> >  only warns for functions visible in other compilation units or (in the case of
> >  @code{pure} and @code{const}) if it cannot prove that the function returns
> >  normally. A function returns normally if it doesn't contain an infinite loop or
> > diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> > index 6e9530c3d7f..998b7608d78 100644
> > --- a/gcc/gimple-range-fold.cc
> > +++ b/gcc/gimple-range-fold.cc
> > @@ -44,6 +44,11 @@ along with GCC; see the file COPYING3.  If not see
> >  #include "value-query.h"
> >  #include "gimple-range-op.h"
> >  #include "gimple-range.h"
> > +#include "cgraph.h"
> > +#include "alloc-pool.h"
> > +#include "symbol-summary.h"
> > +#include "ipa-utils.h"
> > +#include "ipa-prop.h"
> >  // Construct a fur_source, and set the m_query field.
> >
> >  fur_source::fur_source (range_query *q)
> > @@ -1013,6 +1018,25 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
> >    else
> >      r.set_varying (type);
> >
> > +  tree callee = gimple_call_fndecl (call);
> > +  if (callee
> > +      && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), type))
> > +    {
> > +      Value_Range val;
> > +      if (ipa_return_value_range (val, callee))
> > +       {
> > +         r.intersect (val);
> > +         if (dump_file && (dump_flags & TDF_DETAILS))
> > +           {
> > +             fprintf (dump_file, "Using return value range of ");
> > +             print_generic_expr (dump_file, callee, TDF_SLIM);
> > +             fprintf (dump_file, ": ");
> > +             val.dump (dump_file);
> > +             fprintf (dump_file, "\n");
> > +           }
> > +       }
> > +    }
> > +
> >    // If there is an LHS, intersect that with what is known.
> >    if (lhs)
> >      {
> > diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
> > index 7de2b788185..e77bc9c340b 100644
> > --- a/gcc/ipa-prop.cc
> > +++ b/gcc/ipa-prop.cc
> > @@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x)
> >    return gt_ggc_mx ((ipa_vr *) x);
> >  }
> >
> > +/* Analysis summery of function call return value.  */
> > +struct GTY(()) ipa_return_value_summary
> > +{
> > +  /* Known value range.
> > +     This needs to be wrapped in struccture due to specific way
> > +     we allocate ipa_vr. */
> > +  ipa_vr *vr;
> > +};
> > +
> > +/* Function summary for return values.  */
> > +class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
> > +{
> > +public:
> > +  ipa_return_value_sum_t (symbol_table *table, bool ggc):
> > +    function_summary <ipa_return_value_summary *> (table, ggc) { }
> > +
> > +  /* Hook that is called by summary when a node is duplicated.  */
> > +  void duplicate (cgraph_node *,
> > +                 cgraph_node *,
> > +                 ipa_return_value_summary *data,
> > +                 ipa_return_value_summary *data2) final override
> > +  {
> > +    *data2=*data;
> > +  }
> > +};
> > +
> > +/* Variable hoding the return value summary.  */
> > +static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
> > +
> >
> >  /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
> >     with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
> > @@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node)
> >    return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
> >  }
> >
> > +/* Record that current function return value range is VAL.  */
> > +
> > +void
> > +ipa_record_return_value_range (Value_Range val)
> > +{
> > +  cgraph_node *n = cgraph_node::get (current_function_decl);
> > +  if (!ipa_return_value_sum)
> > +    {
> > +      if (!ipa_vr_hash_table)
> > +       ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
> > +      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
> > +             ipa_return_value_sum_t (symtab, true);
> > +      ipa_return_value_sum->disable_insertion_hook ();
> > +    }
> > +  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
> > +  if (dump_file && (dump_flags & TDF_DETAILS))
> > +    {
> > +      fprintf (dump_file, "Recording return range ");
> > +      val.dump (dump_file);
> > +      fprintf (dump_file, "\n");
> > +    }
> > +}
> > +
> > +/* Return true if value range of DECL is known and if so initialize RANGE.  */
> > +
> > +bool
> > +ipa_return_value_range (Value_Range &range, tree decl)
> > +{
> > +  cgraph_node *n = cgraph_node::get (decl);
> > +  if (!n || !ipa_return_value_sum)
> > +    return false;
> > +  enum availability avail;
> > +  n = n->ultimate_alias_target (&avail);
> > +  if (avail < AVAIL_AVAILABLE)
> > +    return false;
> > +  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
> > +    return false;
> > +  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
> > +  if (!v)
> > +    return false;
> > +  v->vr->get_vrange (range);
> > +  return true;
> > +}
> > +
> >
> >  #include "gt-ipa-prop.h"
> > diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> > index fcd0e5c638f..5901c805c40 100644
> > --- a/gcc/ipa-prop.h
> > +++ b/gcc/ipa-prop.h
> > @@ -309,7 +309,7 @@ public:
> >    void get_vrange (Value_Range &) const;
> >    bool equal_p (const vrange &) const;
> >    const vrange_storage *storage () const { return m_storage; }
> > -  void streamer_read (lto_input_block *, data_in *);
> > +  void streamer_read (lto_input_block *, class data_in *);
> >    void streamer_write (output_block *) const;
> >    void dump (FILE *) const;
> >
> > @@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
> >      r.set (val, val);
> >  }
> >
> > +bool ipa_return_value_range (Value_Range &range, tree decl);
> > +void ipa_record_return_value_range (Value_Range val);
> > +
> >  #endif /* IPA_PROP_H */
> > diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc
> > index 058a7dd3019..3060ffeefcd 100644
> > --- a/gcc/ipa-pure-const.cc
> > +++ b/gcc/ipa-pure-const.cc
> > @@ -292,6 +292,15 @@ warn_function_cold (tree decl)
> >                          true, warned_about, "cold");
> >  }
> >
> > +void
> > +warn_function_returns_nonnull (tree decl)
> > +{
> > +  static hash_set<tree> *warned_about;
> > +  warned_about
> > +    = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl,
> > +                        true, warned_about, "returns_nonnull");
> > +}
> > +
> >  /* Check to see if the use (or definition when CHECKING_WRITE is true)
> >     variable T is legal in a function that is either pure or const.  */
> >
> > diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
> > index 0eefcf40d44..84728c589ea 100644
> > --- a/gcc/ipa-utils.h
> > +++ b/gcc/ipa-utils.h
> > @@ -105,6 +105,7 @@ tree prevailing_odr_type (tree type);
> >  void enable_odr_based_tbaa (tree type);
> >  bool odr_based_tbaa_p (const_tree type);
> >  void set_type_canonical_for_odr_type (tree type, tree canonical);
> > +void warn_function_returns_nonnull (tree);
> >
> >  void register_odr_enum (tree type);
> >
> > diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
> > index 3ed61627382..5fd49a2552e 100644
> > --- a/gcc/symbol-summary.h
> > +++ b/gcc/symbol-summary.h
> > @@ -71,7 +71,7 @@ public:
> >         = m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this);
> >    }
> >
> > -  /* Enable insertion hook invocation.  */
> > +  /* Disable insertion hook invocation.  */
> >    void disable_insertion_hook ()
> >    {
> >      if (m_symtab_insertion_hook != NULL)
> > diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > index 48a94e09828..1797db6c81c 100644
> > --- a/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > +++ b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > @@ -43,7 +43,7 @@ int C::foo (int i)
> >    return i + 3;
> >  }
> >
> > -int __attribute__ ((noinline,noclone)) get_input(void)
> > +int __attribute__ ((noinline,noclone,noipa)) get_input(void)
> >  {
> >    return 1;
> >  }
> > diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > index f27a264fd1e..b24b2bca5f9 100644
> > --- a/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > +++ b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > @@ -1,7 +1,7 @@
> >  /* Verify that IPA-CP can do devirtualization even if the virtual call
> >     comes from a method that has been early-inlined into a descendant.  */
> >  /* { dg-do run } */
> > -/* { dg-options "-O3 -fdump-ipa-cp"  } */
> > +/* { dg-options "-O3 -fdump-ipa-cp -fno-ipa-vrp"  } */
> >  /* { dg-add-options bind_pic_locally } */
> >
> >  extern "C" void abort (void);
> > diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > index 7f56189eebb..ae121e8a762 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
> >
> >  class A
> >  {
> > diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > index 5a3cca24fa2..03c10f12db2 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
> >
> >  __attribute__ ((noinline))
> >  int zero()
> > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-1.C b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > index 2d988bc6d55..ccb1870ec69 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > @@ -1,7 +1,7 @@
> >  /* Verify that simple virtual calls are inlined even without early
> >     inlining.  */
> >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> >
> >  extern "C" void abort (void);
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-3.C b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > index f756a16bae9..02e7e443fa9 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > @@ -1,7 +1,7 @@
> >  /* Verify that simple virtual calls on an object refrence are inlined
> >     even without early inlining.  */
> >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> >
> >  extern "C" void abort (void);
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-5.C b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > index 6c19907686e..cb889d1e84f 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > @@ -1,7 +1,7 @@
> >  /* Verify that virtual call inlining does not pick a wrong method when
> >     there is a user defined ancestor in an object.  */
> >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> >
> >  extern "C" void abort (void);
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-8.C b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > index bc81abfe347..f29e818e357 100644
> > --- a/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > @@ -1,7 +1,7 @@
> >  /* Verify that virtual calls are inlined (ithout early inlining) even
> >     when their caller is itself indirectly inlined.  */
> >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> >
> >  extern "C" void abort (void);
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/nothrow-1.C b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > index b30b0215924..1f243109619 100644
> > --- a/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > +++ b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized"  } */
> > +/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized -fno-ipa-vrp"  } */
> >  int *ptr;
> >  static int barvar;
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-1.C b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > index 61940c670e7..c18278cae11 100644
> > --- a/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > +++ b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> > +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
> >  int *ptr;
> >  static int barvar;
> >
> > diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-2.C b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > index 6e739de4ade..d5f18bfa9be 100644
> > --- a/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > +++ b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> > +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
> >  int *ptr;
> >  static int barvar;
> >  /* We can not detect A to be const because it may be interposed by unoptimized
> > diff --git a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > index 0294dcc4bfb..c56360ef66e 100644
> > --- a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > +++ b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > @@ -1,5 +1,5 @@
> >  // { dg-lto-do link }
> > -/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details" } } */
> > +/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details -fno-ipa-vrp" } } */
> >  #include "inline-crossmodule-1.h"
> >  int a::key ()
> >  {
> > diff --git a/gcc/testsuite/gcc.c-torture/compile/pr106433.c b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > index b840e5ecd93..e02ad5ffe15 100644
> > --- a/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > +++ b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > @@ -2,7 +2,7 @@
> >
> >  int m, *p;
> >
> > -__attribute__ ((simd)) int
> > +__attribute__ ((simd,noipa)) int
> >  bar (int x)
> >  {
> >    if (x)
> > diff --git a/gcc/testsuite/gcc.c-torture/execute/frame-address.c b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > index 5afa691e409..5950581054d 100644
> > --- a/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > +++ b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > @@ -1,10 +1,10 @@
> >  /* { dg-require-effective-target return_address } */
> >  void abort (void);
> >
> > -int check_fa_work (const char *, const char *) __attribute__((noinline));
> > -int check_fa_mid (const char *) __attribute__((noinline));
> > -int check_fa (char *) __attribute__((noinline));
> > -int how_much (void) __attribute__((noinline));
> > +int check_fa_work (const char *, const char *) __attribute__((noinline,noipa));
> > +int check_fa_mid (const char *) __attribute__((noinline,noipa));
> > +int check_fa (char *) __attribute__((noinline,noipa));
> > +int how_much (void) __attribute__((noinline,noipa));
> >
> >  int check_fa_work (const char *c, const char *f)
> >  {
> > diff --git a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > index 4032ad13e19..155a6829b88 100644
> > --- a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > +++ b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > @@ -1,4 +1,4 @@
> > -/* { dg-options "-O3 -fopt-info-inline-optimized-missed" } */
> > +/* { dg-options "-O3 -fopt-info-inline-optimized-missed -fno-ipa-vrp" } */
> >
> >  static int foo (int a)
> >  {
> > diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > index fad0891283e..cbda6858890 100644
> > --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-ipa-icf-optimized-all"  } */
> > +/* { dg-options "-O2 -fdump-ipa-icf-optimized-all -fno-ipa-vrp"  } */
> >
> >  static int zip();
> >  static int zap();
> > diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > index 57c5262dd4a..a8824d040e5 100644
> > --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > @@ -1,6 +1,6 @@
> >  /* { dg-do link } */
> >  /* { dg-require-alias "" } */
> > -/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized" } */
> > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized -fno-ipa-vrp" } */
> >  /* { dg-require-effective-target lto } */
> >  /* { dg-additional-sources "ipa-icf-38a.c" }*/
> >
> > diff --git a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > index dd58457b629..10b572781c7 100644
> > --- a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > +++ b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile { target { nonpic || pie_enabled } } } */
> > -/* { dg-options "-O3 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
> > +/* { dg-options "-O3 -fno-ipa-vrp -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
> >  void abort (void);
> >  int error_code;
> >  static int val;
> > diff --git a/gcc/testsuite/gcc.dg/ipa/remref-0.c b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > index 6073c028a98..497136e3607 100644
> > --- a/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > +++ b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > @@ -3,7 +3,7 @@
> >  /* { dg-do compile } */
> >  /* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
> >
> > -extern int __attribute__ ((noinline, noclone, used))
> > +extern int __attribute__ ((noinline, noclone, used, noipa))
> >  stuff (int i)
> >  {
> >    return 0;
> > diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > index 455f923f3f4..3f1d1e04619 100644
> > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > @@ -1,4 +1,4 @@
> > -/* { dg-options "-O2 -fdump-ipa-profile" } */
> > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
> >
> >  __attribute__ ((noinline))
> >  int foo()
> > diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > index e6eaeb99810..eed0b1dd08d 100644
> > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > @@ -1,4 +1,4 @@
> > -/* { dg-options "-O2 -fdump-ipa-profile" } */
> > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
> >
> >  #include <unistd.h>
> >
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > index c68a6f91604..dd5022f3b0c 100644
> > --- a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized" } */
> > +/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized -fno-ipa-vrp" } */
> >
> >  void foo(void);
> >  static int a = 1, c;
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > index f05076cafac..3a7c03b27ff 100644
> > --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps" } */
> > +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps -fno-ipa-vrp" } */
> >
> >  typedef struct {
> >    int code;
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> > new file mode 100644
> > index 00000000000..4db52233c5d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> > @@ -0,0 +1,22 @@
> > +/* { dg-do ling } */
> > +/* { dg-options "-O1 -dump-tree-evrp-details" } */
> > +__attribute__ ((__noinline__))
> > +int a(char c)
> > +{
> > +       return c;
> > +}
> > +void link_error ();
> > +
> > +void
> > +test(int d)
> > +{
> > +       if (a(d) > 200)
> > +               link_error ();
> > +}
> > +int
> > +main(int argc, char **argv)
> > +{
> > +       test(argc);
> > +       return 0;
> > +}
> > +/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > index 7f38e8d3852..f7ba16c20bb 100644
> > --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps" } */
> > +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps -fno-ipa-vrp" } */
> >
> >
> >  inline int ten()
> > diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
> > index 917fa873714..82001eff20e 100644
> > --- a/gcc/tree-vrp.cc
> > +++ b/gcc/tree-vrp.cc
> > @@ -52,6 +52,12 @@ along with GCC; see the file COPYING3.  If not see
> >  #include "gimple-fold.h"
> >  #include "tree-dfa.h"
> >  #include "tree-ssa-dce.h"
> > +#include "alloc-pool.h"
> > +#include "cgraph.h"
> > +#include "symbol-summary.h"
> > +#include "ipa-utils.h"
> > +#include "ipa-prop.h"
> > +#include "attribs.h"
> >
> >  // This class is utilized by VRP and ranger to remove __builtin_unreachable
> >  // calls, and reflect any resulting global ranges.
> > @@ -1081,6 +1087,51 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
> >        array_checker.check ();
> >      }
> >
> > +
> > +  if (Value_Range::supports_type_p (TREE_TYPE
> > +                                    (TREE_TYPE (current_function_decl)))
> > +      && flag_ipa_vrp
> > +      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
> > +    {
> > +      edge e;
> > +      edge_iterator ei;
> > +      bool found = false;
> > +      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
> > +      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> > +       if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
> > +         {
> > +           tree retval = gimple_return_retval (ret);
> > +           if (!retval)
> > +             {
> > +               return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
> > +               found = true;
> > +               continue;
> > +             }
> > +           Value_Range r (TREE_TYPE (retval));
> > +           if (ranger->range_of_expr (r, retval, ret)
> > +               && !r.undefined_p ()
> > +               && !r.varying_p ())
> > +             {
> > +               if (!found)
> > +                 return_range = r;
> > +               else
> > +                 return_range.union_ (r);
> > +             }
> > +           else
> > +             return_range.set_varying (TREE_TYPE (retval));
> > +           found = true;
> > +         }
> > +      if (found && !return_range.varying_p ())
> > +       {
> > +         ipa_record_return_value_range (return_range);
> > +         if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
> > +             && return_range.nonzero_p ()
> > +             && cgraph_node::get (current_function_decl)
> > +                       ->add_detected_attribute ("returns_nonnull"))
> > +           warn_function_returns_nonnull (current_function_decl);
> > +       }
> > +    }
> > +
> >    phi_analysis_finalize ();
> >    disable_ranger (fun);
> >    scev_finalize ();
Thomas Schwinge Nov. 21, 2023, 9:35 p.m. UTC | #9
Hi Honza!

On 2023-11-21T15:06:54+0100, Jan Hubicka <hubicka@ucw.cz> wrote:
>> After this patch in addition to the problem already reported about
>> vlda1.c and return-value-range-1.c, [...]

> return-value_range-1.c should be fixed now and I do not have vlda1.c in
> my tree.  I will check.

Typo, I suppose; probably 'gcc.dg/vla-1.c' is meant here:

    PASS: gcc.dg/vla-1.c (test for excess errors)
    [-PASS:-]{+FAIL:+} gcc.dg/vla-1.c scan-tree-dump-times optimized " s=> i" 2

..., and can you please also check if the following also relates to your
commit?  I've not looked into any details, but noticed that you did
adjusted 'gcc.dg/tree-prof/time-profiler-1.c', and
'gcc.dg/tree-prof/time-profiler-2.c', but not
'gcc.dg/tree-prof/time-profiler-3.c':

    @@ -142800,9 +142802,9 @@ PASS: gcc.dg/tree-prof/time-profiler-3.c compilation,  -fprofile-generate -D_PRO
    PASS: gcc.dg/tree-prof/time-profiler-3.c compilation,  -fprofile-use -D_PROFILE_USE
    PASS: gcc.dg/tree-prof/time-profiler-3.c execution,    -fprofile-generate -D_PROFILE_GENERATE
    PASS: gcc.dg/tree-prof/time-profiler-3.c execution,    -fprofile-use -D_PROFILE_USE
    [-PASS:-]{+FAIL:+} gcc.dg/tree-prof/time-profiler-3.c scan-ipa-dump-times profile "Read tp_first_run: 0" 1
    PASS: gcc.dg/tree-prof/time-profiler-3.c scan-ipa-dump-times profile "Read tp_first_run: 1" 1
    [-PASS:-]{+FAIL:+} gcc.dg/tree-prof/time-profiler-3.c scan-ipa-dump-times profile "Read tp_first_run: 2" 1

>> > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
>> > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
>> > @@ -1,4 +1,4 @@
>> > -/* { dg-options "-O2 -fdump-ipa-profile" } */
>> > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */

>> > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
>> > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
>> > @@ -1,4 +1,4 @@
>> > -/* { dg-options "-O2 -fdump-ipa-profile" } */
>> > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Andrew Pinski Nov. 23, 2023, 7:24 a.m. UTC | #10
On Tue, Nov 21, 2023 at 6:07 AM Jan Hubicka <hubicka@ucw.cz> wrote:
>
> > After this patch in addition to the problem already reported about
> > vlda1.c and return-value-range-1.c, we have noticed these regressions
> > on aarch64:
> > Running gcc:gcc.target/aarch64/aarch64.exp ...
> > FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x4667, lsl 16
> > FAIL: gcc.target/aarch64/movk.c scan-assembler movk\tx[0-9]+, 0x7a3d, lsl 32
> >
> > Running gcc:gcc.target/aarch64/simd/simd.exp ...
> > FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
> > fmul[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 1
> > FAIL: gcc.target/aarch64/simd/vmulxd_f64_2.c scan-assembler-times
> > fmulx[ \t]+[dD][0-9]+, ?[dD][0-9]+, ?[dD][0-9]+\n 4
> > FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
> > fmul[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 1
> > FAIL: gcc.target/aarch64/simd/vmulxs_f32_2.c scan-assembler-times
> > fmulx[ \t]+[sS][0-9]+, ?[sS][0-9]+, ?[sS][0-9]+\n 4
>
> Sorry for that - I guess we will see some on various targets.
> This is quite common issue - the testcase is having
> dummy_number_generator function returning constant and prevents
> inlining to avoid constant being visible to compiler.  This no longer
> works, since we get it from the return value range.  This should fix it.
>
> return-value_range-1.c should be fixed now and I do not have vlda1.c in
> my tree.  I will check.

This is the other change that needs to happen I think:
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/vmulx.x
b/gcc/testsuite/gcc.target/aarch64/simd/vmulx.x
index 8968a64a95c..869e7485646 100644
--- a/gcc/testsuite/gcc.target/aarch64/simd/vmulx.x
+++ b/gcc/testsuite/gcc.target/aarch64/simd/vmulx.x
@@ -33,13 +33,13 @@
   while (0)                                                            \

 /* Functions used to return values that won't be optimised away.  */
-float32_t  __attribute__ ((noinline))
+float32_t  __attribute__ ((noipa))
 foo32 ()
 {
   return 1.0;
 }

-float64_t  __attribute__ ((noinline))
+float64_t  __attribute__ ((noipa))
 foo64 ()
 {
   return 1.0;


Thanks,
Andrew Pinski

>
> diff --git a/gcc/testsuite/gcc.target/aarch64/movk.c b/gcc/testsuite/gcc.target/aarch64/movk.c
> index e6e4e3a8961..6b1f3f8ecf5 100644
> --- a/gcc/testsuite/gcc.target/aarch64/movk.c
> +++ b/gcc/testsuite/gcc.target/aarch64/movk.c
> @@ -1,8 +1,9 @@
>  /* { dg-do run } */
> -/* { dg-options "-O2 --save-temps -fno-inline" } */
> +/* { dg-options "-O2 --save-temps" } */
>
>  extern void abort (void);
>
> +__attribute__ ((noipa))
>  long long int
>  dummy_number_generator ()
>  {
>
> >
> > We have already sent you a notification for the regression on arm, but
> > it includes on vla-1.c and return-value-range-1.c.
> > The notification email contains a pointer to the page where we record
> > all the configurations that regress because of this patch:
> >
> > https://linaro.atlassian.net/browse/GNU-1025
> >
> > Can you have a look?
> >
> > Thanks,
> >
> > Christophe
> >
> >
> >
> >
> > > diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> > > index e41e5ad3ae7..71dacf23ce1 100644
> > > --- a/gcc/cgraph.cc
> > > +++ b/gcc/cgraph.cc
> > > @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p)
> > >    return changed;
> > >  }
> > >
> > > +/* Worker to set malloc flag.  */
> > > +static void
> > > +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
> > > +{
> > > +  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
> > > +    {
> > > +      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
> > > +                                        NULL_TREE, DECL_ATTRIBUTES (node->decl));
> > > +      *changed = true;
> > > +    }
> > > +
> > > +  ipa_ref *ref;
> > > +  FOR_EACH_ALIAS (node, ref)
> > > +    {
> > > +      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> > > +      if (alias->get_availability () > AVAIL_INTERPOSABLE)
> > > +       add_detected_attribute_1 (alias, attr, changed);
> > > +    }
> > > +
> > > +  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
> > > +    if (e->caller->thunk
> > > +       && (e->caller->get_availability () > AVAIL_INTERPOSABLE))
> > > +      add_detected_attribute_1 (e->caller, attr, changed);
> > > +}
> > > +
> > > +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
> > > +
> > > +bool
> > > +cgraph_node::add_detected_attribute (const char *attr)
> > > +{
> > > +  bool changed = false;
> > > +
> > > +  if (get_availability () > AVAIL_INTERPOSABLE)
> > > +    add_detected_attribute_1 (this, attr, &changed);
> > > +  else
> > > +    {
> > > +      ipa_ref *ref;
> > > +
> > > +      FOR_EACH_ALIAS (this, ref)
> > > +       {
> > > +         cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
> > > +         if (alias->get_availability () > AVAIL_INTERPOSABLE)
> > > +           add_detected_attribute_1 (alias, attr, &changed);
> > > +       }
> > > +    }
> > > +  return changed;
> > > +}
> > > +
> > >  /* Worker to set noreturng flag.  */
> > >  static void
> > >  set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
> > > diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> > > index cedaaac3a45..cfdd9f693a8 100644
> > > --- a/gcc/cgraph.h
> > > +++ b/gcc/cgraph.h
> > > @@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
> > >
> > >    bool set_pure_flag (bool pure, bool looping);
> > >
> > > +  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
> > > +     if any.  */
> > > +  bool add_detected_attribute (const char *attr);
> > > +
> > >    /* Call callback on function and aliases associated to the function.
> > >       When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
> > >       skipped. */
> > > diff --git a/gcc/common.opt b/gcc/common.opt
> > > index d21db5d4a20..c6599c7147b 100644
> > > --- a/gcc/common.opt
> > > +++ b/gcc/common.opt
> > > @@ -781,6 +781,10 @@ Wsuggest-attribute=malloc
> > >  Common Var(warn_suggest_attribute_malloc) Warning
> > >  Warn about functions which might be candidates for __attribute__((malloc)).
> > >
> > > +Wsuggest-attribute=returns_nonnull
> > > +Common Warning
> > > +Warn about functions which might be candidates for __attribute__((returns_nonnull)).
> > > +
> > >  Wsuggest-final-types
> > >  Common Var(warn_suggest_final_types) Warning
> > >  Warn about C++ polymorphic types where adding final keyword would improve code quality.
> > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > > index 1f109679c70..3c33104df0a 100644
> > > --- a/gcc/doc/invoke.texi
> > > +++ b/gcc/doc/invoke.texi
> > > @@ -8093,7 +8093,7 @@ if the array is referenced as a flexible array member.
> > >
> > >  @opindex Wsuggest-attribute=
> > >  @opindex Wno-suggest-attribute=
> > > -@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
> > > +@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|}
> > >  Warn for cases where adding an attribute may be beneficial. The
> > >  attributes currently supported are listed below.
> > >
> > > @@ -8113,9 +8113,11 @@ attributes currently supported are listed below.
> > >  @itemx -Wsuggest-attribute=noreturn
> > >  @itemx -Wmissing-noreturn
> > >  @itemx -Wsuggest-attribute=malloc
> > > +@itemx -Wsuggest-attribute=returns_nonnull
> > > +@itemx -Wno-suggest-attribute=returns_nonnull
> > >
> > >  Warn about functions that might be candidates for attributes
> > > -@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
> > > +@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler
> > >  only warns for functions visible in other compilation units or (in the case of
> > >  @code{pure} and @code{const}) if it cannot prove that the function returns
> > >  normally. A function returns normally if it doesn't contain an infinite loop or
> > > diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> > > index 6e9530c3d7f..998b7608d78 100644
> > > --- a/gcc/gimple-range-fold.cc
> > > +++ b/gcc/gimple-range-fold.cc
> > > @@ -44,6 +44,11 @@ along with GCC; see the file COPYING3.  If not see
> > >  #include "value-query.h"
> > >  #include "gimple-range-op.h"
> > >  #include "gimple-range.h"
> > > +#include "cgraph.h"
> > > +#include "alloc-pool.h"
> > > +#include "symbol-summary.h"
> > > +#include "ipa-utils.h"
> > > +#include "ipa-prop.h"
> > >  // Construct a fur_source, and set the m_query field.
> > >
> > >  fur_source::fur_source (range_query *q)
> > > @@ -1013,6 +1018,25 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
> > >    else
> > >      r.set_varying (type);
> > >
> > > +  tree callee = gimple_call_fndecl (call);
> > > +  if (callee
> > > +      && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), type))
> > > +    {
> > > +      Value_Range val;
> > > +      if (ipa_return_value_range (val, callee))
> > > +       {
> > > +         r.intersect (val);
> > > +         if (dump_file && (dump_flags & TDF_DETAILS))
> > > +           {
> > > +             fprintf (dump_file, "Using return value range of ");
> > > +             print_generic_expr (dump_file, callee, TDF_SLIM);
> > > +             fprintf (dump_file, ": ");
> > > +             val.dump (dump_file);
> > > +             fprintf (dump_file, "\n");
> > > +           }
> > > +       }
> > > +    }
> > > +
> > >    // If there is an LHS, intersect that with what is known.
> > >    if (lhs)
> > >      {
> > > diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
> > > index 7de2b788185..e77bc9c340b 100644
> > > --- a/gcc/ipa-prop.cc
> > > +++ b/gcc/ipa-prop.cc
> > > @@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x)
> > >    return gt_ggc_mx ((ipa_vr *) x);
> > >  }
> > >
> > > +/* Analysis summery of function call return value.  */
> > > +struct GTY(()) ipa_return_value_summary
> > > +{
> > > +  /* Known value range.
> > > +     This needs to be wrapped in struccture due to specific way
> > > +     we allocate ipa_vr. */
> > > +  ipa_vr *vr;
> > > +};
> > > +
> > > +/* Function summary for return values.  */
> > > +class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
> > > +{
> > > +public:
> > > +  ipa_return_value_sum_t (symbol_table *table, bool ggc):
> > > +    function_summary <ipa_return_value_summary *> (table, ggc) { }
> > > +
> > > +  /* Hook that is called by summary when a node is duplicated.  */
> > > +  void duplicate (cgraph_node *,
> > > +                 cgraph_node *,
> > > +                 ipa_return_value_summary *data,
> > > +                 ipa_return_value_summary *data2) final override
> > > +  {
> > > +    *data2=*data;
> > > +  }
> > > +};
> > > +
> > > +/* Variable hoding the return value summary.  */
> > > +static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
> > > +
> > >
> > >  /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
> > >     with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
> > > @@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node)
> > >    return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
> > >  }
> > >
> > > +/* Record that current function return value range is VAL.  */
> > > +
> > > +void
> > > +ipa_record_return_value_range (Value_Range val)
> > > +{
> > > +  cgraph_node *n = cgraph_node::get (current_function_decl);
> > > +  if (!ipa_return_value_sum)
> > > +    {
> > > +      if (!ipa_vr_hash_table)
> > > +       ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
> > > +      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
> > > +             ipa_return_value_sum_t (symtab, true);
> > > +      ipa_return_value_sum->disable_insertion_hook ();
> > > +    }
> > > +  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
> > > +  if (dump_file && (dump_flags & TDF_DETAILS))
> > > +    {
> > > +      fprintf (dump_file, "Recording return range ");
> > > +      val.dump (dump_file);
> > > +      fprintf (dump_file, "\n");
> > > +    }
> > > +}
> > > +
> > > +/* Return true if value range of DECL is known and if so initialize RANGE.  */
> > > +
> > > +bool
> > > +ipa_return_value_range (Value_Range &range, tree decl)
> > > +{
> > > +  cgraph_node *n = cgraph_node::get (decl);
> > > +  if (!n || !ipa_return_value_sum)
> > > +    return false;
> > > +  enum availability avail;
> > > +  n = n->ultimate_alias_target (&avail);
> > > +  if (avail < AVAIL_AVAILABLE)
> > > +    return false;
> > > +  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
> > > +    return false;
> > > +  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
> > > +  if (!v)
> > > +    return false;
> > > +  v->vr->get_vrange (range);
> > > +  return true;
> > > +}
> > > +
> > >
> > >  #include "gt-ipa-prop.h"
> > > diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> > > index fcd0e5c638f..5901c805c40 100644
> > > --- a/gcc/ipa-prop.h
> > > +++ b/gcc/ipa-prop.h
> > > @@ -309,7 +309,7 @@ public:
> > >    void get_vrange (Value_Range &) const;
> > >    bool equal_p (const vrange &) const;
> > >    const vrange_storage *storage () const { return m_storage; }
> > > -  void streamer_read (lto_input_block *, data_in *);
> > > +  void streamer_read (lto_input_block *, class data_in *);
> > >    void streamer_write (output_block *) const;
> > >    void dump (FILE *) const;
> > >
> > > @@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
> > >      r.set (val, val);
> > >  }
> > >
> > > +bool ipa_return_value_range (Value_Range &range, tree decl);
> > > +void ipa_record_return_value_range (Value_Range val);
> > > +
> > >  #endif /* IPA_PROP_H */
> > > diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc
> > > index 058a7dd3019..3060ffeefcd 100644
> > > --- a/gcc/ipa-pure-const.cc
> > > +++ b/gcc/ipa-pure-const.cc
> > > @@ -292,6 +292,15 @@ warn_function_cold (tree decl)
> > >                          true, warned_about, "cold");
> > >  }
> > >
> > > +void
> > > +warn_function_returns_nonnull (tree decl)
> > > +{
> > > +  static hash_set<tree> *warned_about;
> > > +  warned_about
> > > +    = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl,
> > > +                        true, warned_about, "returns_nonnull");
> > > +}
> > > +
> > >  /* Check to see if the use (or definition when CHECKING_WRITE is true)
> > >     variable T is legal in a function that is either pure or const.  */
> > >
> > > diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
> > > index 0eefcf40d44..84728c589ea 100644
> > > --- a/gcc/ipa-utils.h
> > > +++ b/gcc/ipa-utils.h
> > > @@ -105,6 +105,7 @@ tree prevailing_odr_type (tree type);
> > >  void enable_odr_based_tbaa (tree type);
> > >  bool odr_based_tbaa_p (const_tree type);
> > >  void set_type_canonical_for_odr_type (tree type, tree canonical);
> > > +void warn_function_returns_nonnull (tree);
> > >
> > >  void register_odr_enum (tree type);
> > >
> > > diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
> > > index 3ed61627382..5fd49a2552e 100644
> > > --- a/gcc/symbol-summary.h
> > > +++ b/gcc/symbol-summary.h
> > > @@ -71,7 +71,7 @@ public:
> > >         = m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this);
> > >    }
> > >
> > > -  /* Enable insertion hook invocation.  */
> > > +  /* Disable insertion hook invocation.  */
> > >    void disable_insertion_hook ()
> > >    {
> > >      if (m_symtab_insertion_hook != NULL)
> > > diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > > index 48a94e09828..1797db6c81c 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/devirt-2.C
> > > @@ -43,7 +43,7 @@ int C::foo (int i)
> > >    return i + 3;
> > >  }
> > >
> > > -int __attribute__ ((noinline,noclone)) get_input(void)
> > > +int __attribute__ ((noinline,noclone,noipa)) get_input(void)
> > >  {
> > >    return 1;
> > >  }
> > > diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > > index f27a264fd1e..b24b2bca5f9 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/devirt-7.C
> > > @@ -1,7 +1,7 @@
> > >  /* Verify that IPA-CP can do devirtualization even if the virtual call
> > >     comes from a method that has been early-inlined into a descendant.  */
> > >  /* { dg-do run } */
> > > -/* { dg-options "-O3 -fdump-ipa-cp"  } */
> > > +/* { dg-options "-O3 -fdump-ipa-cp -fno-ipa-vrp"  } */
> > >  /* { dg-add-options bind_pic_locally } */
> > >
> > >  extern "C" void abort (void);
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > > index 7f56189eebb..ae121e8a762 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> > > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
> > >
> > >  class A
> > >  {
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > > index 5a3cca24fa2..03c10f12db2 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-ipa-icf-optimized"  } */
> > > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -fno-ipa-vrp"  } */
> > >
> > >  __attribute__ ((noinline))
> > >  int zero()
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-1.C b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > > index 2d988bc6d55..ccb1870ec69 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-1.C
> > > @@ -1,7 +1,7 @@
> > >  /* Verify that simple virtual calls are inlined even without early
> > >     inlining.  */
> > >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> > >
> > >  extern "C" void abort (void);
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-3.C b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > > index f756a16bae9..02e7e443fa9 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-3.C
> > > @@ -1,7 +1,7 @@
> > >  /* Verify that simple virtual calls on an object refrence are inlined
> > >     even without early inlining.  */
> > >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> > >
> > >  extern "C" void abort (void);
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-5.C b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > > index 6c19907686e..cb889d1e84f 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-5.C
> > > @@ -1,7 +1,7 @@
> > >  /* Verify that virtual call inlining does not pick a wrong method when
> > >     there is a user defined ancestor in an object.  */
> > >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> > >
> > >  extern "C" void abort (void);
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/ivinline-8.C b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > > index bc81abfe347..f29e818e357 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/ivinline-8.C
> > > @@ -1,7 +1,7 @@
> > >  /* Verify that virtual calls are inlined (ithout early inlining) even
> > >     when their caller is itself indirectly inlined.  */
> > >  /* { dg-do run { target { nonpic || pie_enabled } } } */
> > > -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> > > +/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp -fno-ipa-vrp"  } */
> > >
> > >  extern "C" void abort (void);
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/nothrow-1.C b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > > index b30b0215924..1f243109619 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized"  } */
> > > +/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized -fno-ipa-vrp"  } */
> > >  int *ptr;
> > >  static int barvar;
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-1.C b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > > index 61940c670e7..c18278cae11 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/pure-const-1.C
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> > > +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
> > >  int *ptr;
> > >  static int barvar;
> > >
> > > diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-2.C b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > > index 6e739de4ade..d5f18bfa9be 100644
> > > --- a/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > > +++ b/gcc/testsuite/g++.dg/ipa/pure-const-2.C
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-tree-optimized"  } */
> > > +/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-vrp"  } */
> > >  int *ptr;
> > >  static int barvar;
> > >  /* We can not detect A to be const because it may be interposed by unoptimized
> > > diff --git a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > > index 0294dcc4bfb..c56360ef66e 100644
> > > --- a/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > > +++ b/gcc/testsuite/g++.dg/lto/inline-crossmodule-1_0.C
> > > @@ -1,5 +1,5 @@
> > >  // { dg-lto-do link }
> > > -/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details" } } */
> > > +/* { dg-lto-options { "-O2 -fno-early-inlining -fno-implicit-constexpr -flto -fdump-ipa-inline-details -fno-ipa-vrp" } } */
> > >  #include "inline-crossmodule-1.h"
> > >  int a::key ()
> > >  {
> > > diff --git a/gcc/testsuite/gcc.c-torture/compile/pr106433.c b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > > index b840e5ecd93..e02ad5ffe15 100644
> > > --- a/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > > +++ b/gcc/testsuite/gcc.c-torture/compile/pr106433.c
> > > @@ -2,7 +2,7 @@
> > >
> > >  int m, *p;
> > >
> > > -__attribute__ ((simd)) int
> > > +__attribute__ ((simd,noipa)) int
> > >  bar (int x)
> > >  {
> > >    if (x)
> > > diff --git a/gcc/testsuite/gcc.c-torture/execute/frame-address.c b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > > index 5afa691e409..5950581054d 100644
> > > --- a/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > > +++ b/gcc/testsuite/gcc.c-torture/execute/frame-address.c
> > > @@ -1,10 +1,10 @@
> > >  /* { dg-require-effective-target return_address } */
> > >  void abort (void);
> > >
> > > -int check_fa_work (const char *, const char *) __attribute__((noinline));
> > > -int check_fa_mid (const char *) __attribute__((noinline));
> > > -int check_fa (char *) __attribute__((noinline));
> > > -int how_much (void) __attribute__((noinline));
> > > +int check_fa_work (const char *, const char *) __attribute__((noinline,noipa));
> > > +int check_fa_mid (const char *) __attribute__((noinline,noipa));
> > > +int check_fa (char *) __attribute__((noinline,noipa));
> > > +int how_much (void) __attribute__((noinline,noipa));
> > >
> > >  int check_fa_work (const char *c, const char *f)
> > >  {
> > > diff --git a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > > index 4032ad13e19..155a6829b88 100644
> > > --- a/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > > +++ b/gcc/testsuite/gcc.dg/ipa/fopt-info-inline-1.c
> > > @@ -1,4 +1,4 @@
> > > -/* { dg-options "-O3 -fopt-info-inline-optimized-missed" } */
> > > +/* { dg-options "-O3 -fopt-info-inline-optimized-missed -fno-ipa-vrp" } */
> > >
> > >  static int foo (int a)
> > >  {
> > > diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > > index fad0891283e..cbda6858890 100644
> > > --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > > +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-ipa-icf-optimized-all"  } */
> > > +/* { dg-options "-O2 -fdump-ipa-icf-optimized-all -fno-ipa-vrp"  } */
> > >
> > >  static int zip();
> > >  static int zap();
> > > diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > > index 57c5262dd4a..a8824d040e5 100644
> > > --- a/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > > +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-38.c
> > > @@ -1,6 +1,6 @@
> > >  /* { dg-do link } */
> > >  /* { dg-require-alias "" } */
> > > -/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized" } */
> > > +/* { dg-options "-O2 -fdump-ipa-icf-optimized -flto -fdump-tree-optimized -fno-ipa-vrp" } */
> > >  /* { dg-require-effective-target lto } */
> > >  /* { dg-additional-sources "ipa-icf-38a.c" }*/
> > >
> > > diff --git a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > > index dd58457b629..10b572781c7 100644
> > > --- a/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > > +++ b/gcc/testsuite/gcc.dg/ipa/pure-const-1.c
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile { target { nonpic || pie_enabled } } } */
> > > -/* { dg-options "-O3 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
> > > +/* { dg-options "-O3 -fno-ipa-vrp -fdump-tree-local-pure-const1 -fdump-ipa-pure-const -fdump-tree-optimized -fno-early-inlining -fgnu89-inline" } */
> > >  void abort (void);
> > >  int error_code;
> > >  static int val;
> > > diff --git a/gcc/testsuite/gcc.dg/ipa/remref-0.c b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > > index 6073c028a98..497136e3607 100644
> > > --- a/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > > +++ b/gcc/testsuite/gcc.dg/ipa/remref-0.c
> > > @@ -3,7 +3,7 @@
> > >  /* { dg-do compile } */
> > >  /* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
> > >
> > > -extern int __attribute__ ((noinline, noclone, used))
> > > +extern int __attribute__ ((noinline, noclone, used, noipa))
> > >  stuff (int i)
> > >  {
> > >    return 0;
> > > diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > > index 455f923f3f4..3f1d1e04619 100644
> > > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c
> > > @@ -1,4 +1,4 @@
> > > -/* { dg-options "-O2 -fdump-ipa-profile" } */
> > > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
> > >
> > >  __attribute__ ((noinline))
> > >  int foo()
> > > diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > > index e6eaeb99810..eed0b1dd08d 100644
> > > --- a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > > +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c
> > > @@ -1,4 +1,4 @@
> > > -/* { dg-options "-O2 -fdump-ipa-profile" } */
> > > +/* { dg-options "-O2 -fdump-ipa-profile -fno-ipa-vrp" } */
> > >
> > >  #include <unistd.h>
> > >
> > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > > index c68a6f91604..dd5022f3b0c 100644
> > > --- a/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr110269.c
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized" } */
> > > +/* { dg-options "-O2 -fdump-tree-ccp2 -fdump-tree-optimized -fno-ipa-vrp" } */
> > >
> > >  void foo(void);
> > >  static int a = 1, c;
> > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > > index f05076cafac..3a7c03b27ff 100644
> > > --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20701.c
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps" } */
> > > +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fdelete-null-pointer-checks -fno-thread-jumps -fno-ipa-vrp" } */
> > >
> > >  typedef struct {
> > >    int code;
> > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> > > new file mode 100644
> > > index 00000000000..4db52233c5d
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
> > > @@ -0,0 +1,22 @@
> > > +/* { dg-do ling } */
> > > +/* { dg-options "-O1 -dump-tree-evrp-details" } */
> > > +__attribute__ ((__noinline__))
> > > +int a(char c)
> > > +{
> > > +       return c;
> > > +}
> > > +void link_error ();
> > > +
> > > +void
> > > +test(int d)
> > > +{
> > > +       if (a(d) > 200)
> > > +               link_error ();
> > > +}
> > > +int
> > > +main(int argc, char **argv)
> > > +{
> > > +       test(argc);
> > > +       return 0;
> > > +}
> > > +/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */
> > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > > index 7f38e8d3852..f7ba16c20bb 100644
> > > --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp05.c
> > > @@ -1,5 +1,5 @@
> > >  /* { dg-do compile } */
> > > -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps" } */
> > > +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-early-inlining -fno-thread-jumps -fno-ipa-vrp" } */
> > >
> > >
> > >  inline int ten()
> > > diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
> > > index 917fa873714..82001eff20e 100644
> > > --- a/gcc/tree-vrp.cc
> > > +++ b/gcc/tree-vrp.cc
> > > @@ -52,6 +52,12 @@ along with GCC; see the file COPYING3.  If not see
> > >  #include "gimple-fold.h"
> > >  #include "tree-dfa.h"
> > >  #include "tree-ssa-dce.h"
> > > +#include "alloc-pool.h"
> > > +#include "cgraph.h"
> > > +#include "symbol-summary.h"
> > > +#include "ipa-utils.h"
> > > +#include "ipa-prop.h"
> > > +#include "attribs.h"
> > >
> > >  // This class is utilized by VRP and ranger to remove __builtin_unreachable
> > >  // calls, and reflect any resulting global ranges.
> > > @@ -1081,6 +1087,51 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
> > >        array_checker.check ();
> > >      }
> > >
> > > +
> > > +  if (Value_Range::supports_type_p (TREE_TYPE
> > > +                                    (TREE_TYPE (current_function_decl)))
> > > +      && flag_ipa_vrp
> > > +      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
> > > +    {
> > > +      edge e;
> > > +      edge_iterator ei;
> > > +      bool found = false;
> > > +      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
> > > +      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> > > +       if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
> > > +         {
> > > +           tree retval = gimple_return_retval (ret);
> > > +           if (!retval)
> > > +             {
> > > +               return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
> > > +               found = true;
> > > +               continue;
> > > +             }
> > > +           Value_Range r (TREE_TYPE (retval));
> > > +           if (ranger->range_of_expr (r, retval, ret)
> > > +               && !r.undefined_p ()
> > > +               && !r.varying_p ())
> > > +             {
> > > +               if (!found)
> > > +                 return_range = r;
> > > +               else
> > > +                 return_range.union_ (r);
> > > +             }
> > > +           else
> > > +             return_range.set_varying (TREE_TYPE (retval));
> > > +           found = true;
> > > +         }
> > > +      if (found && !return_range.varying_p ())
> > > +       {
> > > +         ipa_record_return_value_range (return_range);
> > > +         if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
> > > +             && return_range.nonzero_p ()
> > > +             && cgraph_node::get (current_function_decl)
> > > +                       ->add_detected_attribute ("returns_nonnull"))
> > > +           warn_function_returns_nonnull (current_function_decl);
> > > +       }
> > > +    }
> > > +
> > >    phi_analysis_finalize ();
> > >    disable_ranger (fun);
> > >    scev_finalize ();
diff mbox series

Patch

diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index e41e5ad3ae7..71dacf23ce1 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -2629,6 +2629,54 @@  cgraph_node::set_malloc_flag (bool malloc_p)
   return changed;
 }
 
+/* Worker to set malloc flag.  */
+static void
+add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed)
+{
+  if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl)))
+    {
+      DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr),
+					 NULL_TREE, DECL_ATTRIBUTES (node->decl));
+      *changed = true;
+    }
+
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (node, ref)
+    {
+      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+      if (alias->get_availability () > AVAIL_INTERPOSABLE)
+	add_detected_attribute_1 (alias, attr, changed);
+    }
+
+  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+    if (e->caller->thunk
+	&& (e->caller->get_availability () > AVAIL_INTERPOSABLE))
+      add_detected_attribute_1 (e->caller, attr, changed);
+}
+
+/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
+
+bool
+cgraph_node::add_detected_attribute (const char *attr)
+{
+  bool changed = false;
+
+  if (get_availability () > AVAIL_INTERPOSABLE)
+    add_detected_attribute_1 (this, attr, &changed);
+  else
+    {
+      ipa_ref *ref;
+
+      FOR_EACH_ALIAS (this, ref)
+	{
+	  cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+	  if (alias->get_availability () > AVAIL_INTERPOSABLE)
+	    add_detected_attribute_1 (alias, attr, &changed);
+	}
+    }
+  return changed;
+}
+
 /* Worker to set noreturng flag.  */
 static void
 set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed)
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cedaaac3a45..cfdd9f693a8 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1190,6 +1190,10 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
 
   bool set_pure_flag (bool pure, bool looping);
 
+  /* Add attribute ATTR to cgraph_node's decl and on aliases of the node
+     if any.  */
+  bool add_detected_attribute (const char *attr);
+
   /* Call callback on function and aliases associated to the function.
      When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
      skipped. */
diff --git a/gcc/common.opt b/gcc/common.opt
index d21db5d4a20..0be4f02677c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -781,6 +781,10 @@  Wsuggest-attribute=malloc
 Common Var(warn_suggest_attribute_malloc) Warning
 Warn about functions which might be candidates for __attribute__((malloc)).
 
+Wsuggest-attribute=returns_nonnull
+Common Var(warn_suggest_attribute_malloc) Warning
+Warn about functions which might be candidates for __attribute__((malloc)).
+
 Wsuggest-final-types
 Common Var(warn_suggest_final_types) Warning
 Warn about C++ polymorphic types where adding final keyword would improve code quality.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 557d613a1e6..b9e98843613 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -8092,7 +8092,7 @@  if the array is referenced as a flexible array member.
 
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
-@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
+@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|}
 Warn for cases where adding an attribute may be beneficial. The
 attributes currently supported are listed below.
 
@@ -8112,9 +8112,11 @@  attributes currently supported are listed below.
 @itemx -Wsuggest-attribute=noreturn
 @itemx -Wmissing-noreturn
 @itemx -Wsuggest-attribute=malloc
+@itemx -Wsuggest-attribute=returns_nonnull
+@itemx -Wno-suggest-attribute=returns_nonnull
 
 Warn about functions that might be candidates for attributes
-@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
+@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler
 only warns for functions visible in other compilation units or (in the case of
 @code{pure} and @code{const}) if it cannot prove that the function returns
 normally. A function returns normally if it doesn't contain an infinite loop or
diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index 6e9530c3d7f..0af3fa1b008 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -44,6 +44,11 @@  along with GCC; see the file COPYING3.  If not see
 #include "value-query.h"
 #include "gimple-range-op.h"
 #include "gimple-range.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "ipa-utils.h"
+#include "ipa-prop.h"
 // Construct a fur_source, and set the m_query field.
 
 fur_source::fur_source (range_query *q)
@@ -1013,6 +1018,24 @@  fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &)
   else
     r.set_varying (type);
 
+  tree callee = gimple_call_fndecl (call);
+  if (callee)
+    {
+      Value_Range val;
+      if (ipa_return_value_range (val, callee))
+	{
+	  r.intersect (val);
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Using return value range of ");
+	      print_generic_expr (dump_file, callee, TDF_SLIM);
+	      fprintf (dump_file, ": ");
+	      val.dump (dump_file);
+	      fprintf (dump_file, "\n");
+	    }
+	}
+    }
+
   // If there is an LHS, intersect that with what is known.
   if (lhs)
     {
diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
index 7de2b788185..e77bc9c340b 100644
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
@@ -237,6 +237,35 @@  gt_ggc_mx (ipa_vr *&x)
   return gt_ggc_mx ((ipa_vr *) x);
 }
 
+/* Analysis summery of function call return value.  */
+struct GTY(()) ipa_return_value_summary
+{
+  /* Known value range.
+     This needs to be wrapped in struccture due to specific way
+     we allocate ipa_vr. */
+  ipa_vr *vr;
+};
+
+/* Function summary for return values.  */
+class ipa_return_value_sum_t : public function_summary <ipa_return_value_summary *>
+{
+public:
+  ipa_return_value_sum_t (symbol_table *table, bool ggc):
+    function_summary <ipa_return_value_summary *> (table, ggc) { }
+
+  /* Hook that is called by summary when a node is duplicated.  */
+  void duplicate (cgraph_node *,
+		  cgraph_node *,
+		  ipa_return_value_summary *data,
+		  ipa_return_value_summary *data2) final override
+  {
+    *data2=*data;
+  }
+};
+
+/* Variable hoding the return value summary.  */
+static GTY(()) function_summary <ipa_return_value_summary *> *ipa_return_value_sum;
+
 
 /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
    with NODE should prevent us from analyzing it for the purposes of IPA-CP.  */
@@ -5915,5 +5944,49 @@  ipcp_transform_function (struct cgraph_node *node)
   return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
 }
 
+/* Record that current function return value range is VAL.  */
+
+void
+ipa_record_return_value_range (Value_Range val)
+{
+  cgraph_node *n = cgraph_node::get (current_function_decl);
+  if (!ipa_return_value_sum)
+    {
+      if (!ipa_vr_hash_table)
+	ipa_vr_hash_table = hash_table<ipa_vr_ggc_hash_traits>::create_ggc (37);
+      ipa_return_value_sum = new (ggc_alloc_no_dtor <ipa_return_value_sum_t> ())
+	      ipa_return_value_sum_t (symtab, true);
+      ipa_return_value_sum->disable_insertion_hook ();
+    }
+  ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Recording return range ");
+      val.dump (dump_file);
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Return true if value range of DECL is known and if so initialize RANGE.  */
+
+bool
+ipa_return_value_range (Value_Range &range, tree decl)
+{
+  cgraph_node *n = cgraph_node::get (decl);
+  if (!n || !ipa_return_value_sum)
+    return false;
+  enum availability avail;
+  n = n->ultimate_alias_target (&avail);
+  if (avail < AVAIL_AVAILABLE)
+    return false;
+  if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl)))
+    return false;
+  ipa_return_value_summary *v = ipa_return_value_sum->get (n);
+  if (!v)
+    return false;
+  v->vr->get_vrange (range);
+  return true;
+}
+
 
 #include "gt-ipa-prop.h"
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index fcd0e5c638f..5901c805c40 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -309,7 +309,7 @@  public:
   void get_vrange (Value_Range &) const;
   bool equal_p (const vrange &) const;
   const vrange_storage *storage () const { return m_storage; }
-  void streamer_read (lto_input_block *, data_in *);
+  void streamer_read (lto_input_block *, class data_in *);
   void streamer_write (output_block *) const;
   void dump (FILE *) const;
 
@@ -1274,4 +1274,7 @@  ipa_range_set_and_normalize (vrange &r, tree val)
     r.set (val, val);
 }
 
+bool ipa_return_value_range (Value_Range &range, tree decl);
+void ipa_record_return_value_range (Value_Range val);
+
 #endif /* IPA_PROP_H */
diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc
index 058a7dd3019..3060ffeefcd 100644
--- a/gcc/ipa-pure-const.cc
+++ b/gcc/ipa-pure-const.cc
@@ -292,6 +292,15 @@  warn_function_cold (tree decl)
 			 true, warned_about, "cold");
 }
 
+void
+warn_function_returns_nonnull (tree decl)
+{
+  static hash_set<tree> *warned_about;
+  warned_about
+    = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl,
+			 true, warned_about, "returns_nonnull");
+}
+
 /* Check to see if the use (or definition when CHECKING_WRITE is true)
    variable T is legal in a function that is either pure or const.  */
 
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 0eefcf40d44..84728c589ea 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -105,6 +105,7 @@  tree prevailing_odr_type (tree type);
 void enable_odr_based_tbaa (tree type);
 bool odr_based_tbaa_p (const_tree type);
 void set_type_canonical_for_odr_type (tree type, tree canonical);
+void warn_function_returns_nonnull (tree);
 
 void register_odr_enum (tree type);
 
diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
index 3ed61627382..5fd49a2552e 100644
--- a/gcc/symbol-summary.h
+++ b/gcc/symbol-summary.h
@@ -71,7 +71,7 @@  public:
 	= m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this);
   }
 
-  /* Enable insertion hook invocation.  */
+  /* Disable insertion hook invocation.  */
   void disable_insertion_hook ()
   {
     if (m_symtab_insertion_hook != NULL)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
new file mode 100644
index 00000000000..4db52233c5d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c
@@ -0,0 +1,22 @@ 
+/* { dg-do ling } */
+/* { dg-options "-O1 -dump-tree-evrp-details" } */
+__attribute__ ((__noinline__))
+int a(char c)
+{
+	return c;
+}
+void link_error ();
+
+void
+test(int d)
+{
+	if (a(d) > 200)
+		link_error ();
+}
+int
+main(int argc, char **argv)
+{
+	test(argc);
+	return 0;
+}
+/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index 917fa873714..11a382170d8 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -52,6 +52,11 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "tree-dfa.h"
 #include "tree-ssa-dce.h"
+#include "alloc-pool.h"
+#include "cgraph.h"
+#include "symbol-summary.h"
+#include "ipa-utils.h"
+#include "ipa-prop.h"
 
 // This class is utilized by VRP and ranger to remove __builtin_unreachable
 // calls, and reflect any resulting global ranges.
@@ -1081,6 +1086,47 @@  execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
       array_checker.check ();
     }
 
+
+  if (Value_Range::supports_type_p (TREE_TYPE (TREE_TYPE (current_function_decl))))
+    {
+      edge e;
+      edge_iterator ei;
+      bool found = false;
+      Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl)));
+      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+	if (greturn *ret = dyn_cast <greturn *> (*gsi_last_bb (e->src)))
+	  {
+	    tree retval = gimple_return_retval (ret);
+	    if (!retval)
+	      {
+		return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl)));
+		found = true;
+		continue;
+	      }
+	    Value_Range r (TREE_TYPE (retval));
+	    if (ranger->range_of_expr (r, retval, ret)
+		&& !r.undefined_p ()
+		&& !r.varying_p ())
+	      {
+		if (!found)
+		  return_range = r;
+		else
+		  return_range.union_ (r);
+	      }
+	    else
+	      return_range.set_varying (TREE_TYPE (retval));
+	    found = true;
+	  }
+      if (found && !return_range.varying_p ())
+	{
+	  ipa_record_return_value_range (return_range);
+	  if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
+	      && return_range.nonzero_p ()
+	      && cgraph_node::get (current_function_decl)->add_detected_attribute ("returns_nonnull"))
+	    warn_function_returns_nonnull (current_function_decl);
+	}
+    }
+
   phi_analysis_finalize ();
   disable_ranger (fun);
   scev_finalize ();