public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-5126] Extend modref by side-effect analysis
@ 2021-11-10 15:02 Jan Hubicka
  0 siblings, 0 replies; only message in thread
From: Jan Hubicka @ 2021-11-10 15:02 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:992644c3511acd58248db784f1ac43e2f053ebcc

commit r12-5126-g992644c3511acd58248db784f1ac43e2f053ebcc
Author: Jan Hubicka <jh@suse.cz>
Date:   Wed Nov 10 16:00:40 2021 +0100

    Extend modref by side-effect analysis
    
    Make modref to also collect info whether function has side
    effects.  This allows pure/const function detection and also handling
    functions which do store some memory in similar way as we handle
    pure/consts now.
    
    The code is symmetric to what ipa-pure-const does.  Modref is actually more
    capable on proving that a given function is pure/const (since it understands
    that non-pure function can be called when it only modifies data on stack)
    so we could retire ipa-pure-const's pure-const discovery at some point.
    
    However this patch only does the anlaysis - the consumers of this flag
    will come next.
    
    Bootstrapped/regtested x86_64-linux. I plan to commit it later today
    if there are no complains.
    
    gcc/ChangeLog:
    
            * ipa-modref.c: Include tree-eh.h
            (modref_summary::modref_summary): Initialize side_effects.
            (struct modref_summary_lto): New bool field side_effects.
            (modref_summary_lto::modref_summary_lto): Initialize side_effects.
            (modref_summary::dump): Dump side_effects.
            (modref_summary_lto::dump): Dump side_effects.
            (merge_call_side_effects): Merge side effects.
            (process_fnspec): Calls to non-const/pure or looping
            function is a side effect.
            (analyze_call): Self-recursion is a side-effect; handle
            special builtins.
            (analyze_load): Watch for volatile and throwing memory.
            (analyze_store): Likewise.
            (analyze_stmt): Watch for volatitle asm.
            (analyze_function): Handle side_effects.
            (modref_summaries::duplicate): Duplicate side_effects.
            (modref_summaries_lto::duplicate): Likewise.
            (modref_write): Stream side_effects.
            (read_section): Likewise.
            (update_signature): Update.
            (propagate_unknown_call): Handle side_effects.
            (modref_propagate_in_scc): Likewise.
            * ipa-modref.h (struct modref_summary): Add side_effects.
            * ipa-pure-const.c (special_builtin_state): Rename to ...
            (builtin_safe_for_const_function_p): ... this one.
            (check_call): Update.
            (finite_function_p): Break out from ...
            (propagate_pure_const): ... here
            * ipa-utils.h (finite_function): Declare.

Diff:
---
 gcc/ipa-modref.c     | 163 +++++++++++++++++++++++++++++++++++++++++++++++++--
 gcc/ipa-modref.h     |   1 +
 gcc/ipa-pure-const.c | 108 ++++++++++++++++++----------------
 gcc/ipa-utils.h      |   4 ++
 4 files changed, 219 insertions(+), 57 deletions(-)

diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 14c3894d7ba..f8b7b900527 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -87,6 +87,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssanames.h"
 #include "attribs.h"
 #include "tree-cfg.h"
+#include "tree-eh.h"
 
 
 namespace {
@@ -275,7 +276,7 @@ static GTY(()) fast_function_summary <modref_summary_lto *, va_gc>
 
 modref_summary::modref_summary ()
   : loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
-    writes_errno (false)
+    writes_errno (false), side_effects (false)
 {
 }
 
@@ -373,6 +374,7 @@ struct GTY(()) modref_summary_lto
   eaf_flags_t retslot_flags;
   eaf_flags_t static_chain_flags;
   bool writes_errno;
+  bool side_effects;
 
   modref_summary_lto ();
   ~modref_summary_lto ();
@@ -384,7 +386,7 @@ struct GTY(()) modref_summary_lto
 
 modref_summary_lto::modref_summary_lto ()
   : loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
-    writes_errno (false)
+    writes_errno (false), side_effects (false)
 {
 }
 
@@ -617,6 +619,8 @@ modref_summary::dump (FILE *out)
     }
   if (writes_errno)
     fprintf (out, "  Writes errno\n");
+  if (side_effects)
+    fprintf (out, "  Side effects\n");
   if (arg_flags.length ())
     {
       for (unsigned int i = 0; i < arg_flags.length (); i++)
@@ -649,6 +653,8 @@ modref_summary_lto::dump (FILE *out)
   dump_lto_records (stores, out);
   if (writes_errno)
     fprintf (out, "  Writes errno\n");
+  if (side_effects)
+    fprintf (out, "  Side effects\n");
   if (arg_flags.length ())
     {
       for (unsigned int i = 0; i < arg_flags.length (); i++)
@@ -982,6 +988,12 @@ merge_call_side_effects (modref_summary *cur_summary,
 	  changed = true;
 	}
     }
+  if (!cur_summary->side_effects
+      && callee_summary->side_effects)
+    {
+      cur_summary->side_effects = true;
+      changed = true;
+    }
   return changed;
 }
 
@@ -1077,6 +1089,18 @@ process_fnspec (modref_summary *cur_summary,
 		gcall *call, bool ignore_stores)
 {
   attr_fnspec fnspec = gimple_call_fnspec (call);
+  int flags = gimple_call_flags (call);
+
+  if (!(flags & (ECF_CONST | ECF_NOVOPS))
+      || (flags & ECF_LOOPING_CONST_OR_PURE)
+      || (cfun->can_throw_non_call_exceptions
+	  && stmt_could_throw_p (cfun, call)))
+    {
+      if (cur_summary)
+	cur_summary->side_effects = true;
+      if (cur_summary_lto)
+	cur_summary_lto->side_effects = true;
+    }
   if (!fnspec.known_p ())
     {
       if (dump_file && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
@@ -1214,6 +1238,10 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
   if (recursive_call_p (current_function_decl, callee))
     {
       recursive_calls->safe_push (stmt);
+      if (cur_summary)
+	cur_summary->side_effects = true;
+      if (cur_summary_lto)
+	cur_summary_lto->side_effects = true;
       if (dump_file)
 	fprintf (dump_file, " - Skipping recursive call.\n");
       return true;
@@ -1224,6 +1252,20 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
   /* Get the function symbol and its availability.  */
   enum availability avail;
   callee_node = callee_node->function_symbol (&avail);
+  bool looping;
+  if (builtin_safe_for_const_function_p (&looping, callee))
+    {
+      if (looping)
+	{
+	  if (cur_summary)
+	    cur_summary->side_effects = true;
+	  if (cur_summary_lto)
+	    cur_summary_lto->side_effects = true;
+	}
+      if (dump_file)
+	fprintf (dump_file, " - Bulitin is safe for const.\n");
+      return true;
+    }
   if (avail <= AVAIL_INTERPOSABLE)
     {
       if (dump_file)
@@ -1270,6 +1312,18 @@ analyze_load (gimple *, tree, tree op, void *data)
       fprintf (dump_file, "\n");
     }
 
+  if (TREE_THIS_VOLATILE (op)
+      || (cfun->can_throw_non_call_exceptions
+	  && tree_could_throw_p (op)))
+    {
+      if (dump_file)
+	fprintf (dump_file, " (volatile or can throw; marking side effects) ");
+      if (summary)
+	summary->side_effects = true;
+      if (summary_lto)
+	summary_lto->side_effects = true;
+    }
+
   if (!record_access_p (op))
     return false;
 
@@ -1298,6 +1352,18 @@ analyze_store (gimple *, tree, tree op, void *data)
       fprintf (dump_file, "\n");
     }
 
+  if (TREE_THIS_VOLATILE (op)
+      || (cfun->can_throw_non_call_exceptions
+	  && tree_could_throw_p (op)))
+    {
+      if (dump_file)
+	fprintf (dump_file, " (volatile or can throw; marking side effects) ");
+      if (summary)
+	summary->side_effects = true;
+      if (summary_lto)
+	summary_lto->side_effects = true;
+    }
+
   if (!record_access_p (op))
     return false;
 
@@ -1334,6 +1400,15 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
   switch (gimple_code (stmt))
    {
    case GIMPLE_ASM:
+      if (gimple_asm_volatile_p (as_a <gasm *> (stmt))
+	  || (cfun->can_throw_non_call_exceptions
+	      && stmt_could_throw_p (cfun, stmt)))
+	{
+	  if (summary)
+	    summary->side_effects = true;
+	  if (summary_lto)
+	    summary_lto->side_effects = true;
+	}
      /* If the ASM statement does not read nor write memory, there's nothing
 	to do.  Otherwise just give up.  */
      if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (stmt)))
@@ -1365,7 +1440,14 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
       }
      return true;
    default:
-     /* Nothing to do for other types of statements.  */
+     if (cfun->can_throw_non_call_exceptions
+	 && stmt_could_throw_p (cfun, stmt))
+	{
+	  if (summary)
+	    summary->side_effects = true;
+	  if (summary_lto)
+	    summary_lto->side_effects = true;
+	}
      return true;
    }
 }
@@ -2616,6 +2698,7 @@ analyze_function (function *f, bool ipa)
 						    param_modref_max_refs,
 						    param_modref_max_accesses);
       summary->writes_errno = false;
+      summary->side_effects = false;
     }
   if (lto)
     {
@@ -2630,6 +2713,7 @@ analyze_function (function *f, bool ipa)
 				  param_modref_max_refs,
 				  param_modref_max_accesses);
       summary_lto->writes_errno = false;
+      summary_lto->side_effects = false;
     }
 
   analyze_parms (summary, summary_lto, ipa,
@@ -2700,6 +2784,12 @@ analyze_function (function *f, bool ipa)
       summaries_lto->remove (fnode);
       summary_lto = NULL;
     }
+  if (summary && !summary->global_memory_written_p () && !summary->side_effects
+      && !finite_function_p ())
+    summary->side_effects = true;
+  if (summary_lto && !summary_lto->side_effects && !finite_function_p ())
+    summary_lto->side_effects = true;
+
   if (ipa && !summary && !summary_lto)
     remove_modref_edge_summaries (fnode);
 
@@ -2904,6 +2994,7 @@ modref_summaries::duplicate (cgraph_node *, cgraph_node *dst,
 			 src_data->loads->max_accesses);
   dst_data->loads->copy_from (src_data->loads);
   dst_data->writes_errno = src_data->writes_errno;
+  dst_data->side_effects = src_data->side_effects;
   if (src_data->arg_flags.length ())
     dst_data->arg_flags = src_data->arg_flags.copy ();
   dst_data->retslot_flags = src_data->retslot_flags;
@@ -2931,6 +3022,7 @@ modref_summaries_lto::duplicate (cgraph_node *, cgraph_node *,
 			 src_data->loads->max_accesses);
   dst_data->loads->copy_from (src_data->loads);
   dst_data->writes_errno = src_data->writes_errno;
+  dst_data->side_effects = src_data->side_effects;
   if (src_data->arg_flags.length ())
     dst_data->arg_flags = src_data->arg_flags.copy ();
   dst_data->retslot_flags = src_data->retslot_flags;
@@ -3259,6 +3351,7 @@ modref_write ()
 
 	  struct bitpack_d bp = bitpack_create (ob->main_stream);
 	  bp_pack_value (&bp, r->writes_errno, 1);
+	  bp_pack_value (&bp, r->side_effects, 1);
 	  if (!flag_wpa)
 	    {
 	      for (cgraph_edge *e = cnode->indirect_calls;
@@ -3328,9 +3421,15 @@ read_section (struct lto_file_decl_data *file_data, const char *data,
 	modref_sum = optimization_summaries->get_create (node);
 
       if (modref_sum)
-	modref_sum->writes_errno = false;
+	{
+	  modref_sum->writes_errno = false;
+	  modref_sum->side_effects = false;
+	}
       if (modref_sum_lto)
-	modref_sum_lto->writes_errno = false;
+	{
+	  modref_sum_lto->writes_errno = false;
+	  modref_sum_lto->side_effects = false;
+	}
 
       gcc_assert (!modref_sum || (!modref_sum->loads
 				  && !modref_sum->stores));
@@ -3375,6 +3474,13 @@ read_section (struct lto_file_decl_data *file_data, const char *data,
 	  if (modref_sum_lto)
 	    modref_sum_lto->writes_errno = true;
 	}
+      if (bp_unpack_value (&bp, 1))
+	{
+	  if (modref_sum)
+	    modref_sum->side_effects = true;
+	  if (modref_sum_lto)
+	    modref_sum_lto->side_effects = true;
+	}
       if (!flag_ltrans)
 	{
 	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
@@ -3523,7 +3629,7 @@ update_signature (struct cgraph_node *node)
 
   map.reserve (max + 1);
   for (i = 0; i <= max; i++)
-    map.quick_push (-1);
+    map.quick_push (MODREF_UNKNOWN_PARM);
   FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
     {
       int idx = info->param_adjustments->get_original_index (i);
@@ -3862,6 +3968,39 @@ propagate_unknown_call (cgraph_node *node,
   bool changed = false;
   class fnspec_summary *fnspec_sum = fnspec_summaries->get (e);
   auto_vec <modref_parm_map, 32> parm_map;
+  bool looping;
+
+  if (e->callee
+      && builtin_safe_for_const_function_p (&looping, e->callee->decl))
+    {
+      if (cur_summary && !cur_summary->side_effects)
+	{
+	  cur_summary->side_effects = true;
+	  changed = true;
+	}
+      if (cur_summary_lto && !cur_summary_lto->side_effects)
+	{
+	  cur_summary_lto->side_effects = true;
+	  changed = true;
+	}
+      return changed;
+    }
+
+  if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS))
+      || (ecf_flags & ECF_LOOPING_CONST_OR_PURE))
+    {
+      if (cur_summary && !cur_summary->side_effects)
+	{
+	  cur_summary->side_effects = true;
+	  changed = true;
+	}
+      if (cur_summary_lto && !cur_summary_lto->side_effects)
+	{
+	  cur_summary_lto->side_effects = true;
+	  changed = true;
+	}
+    }
+
   if (fnspec_sum
       && compute_parm_map (e, &parm_map))
     {
@@ -4126,6 +4265,12 @@ modref_propagate_in_scc (cgraph_node *component_node)
 		  changed |= cur_summary->loads->merge
 				  (callee_summary->loads, &parm_map,
 				   &chain_map, !first);
+		  if (!cur_summary->side_effects
+		      && callee_summary->side_effects)
+		    {
+		      cur_summary->side_effects = true;
+		      changed = true;
+		    }
 		  if (!ignore_stores)
 		    {
 		      changed |= cur_summary->stores->merge
@@ -4144,6 +4289,12 @@ modref_propagate_in_scc (cgraph_node *component_node)
 		  changed |= cur_summary_lto->loads->merge
 				  (callee_summary_lto->loads, &parm_map,
 				   &chain_map, !first);
+		  if (!cur_summary_lto->side_effects
+		      && callee_summary_lto->side_effects)
+		    {
+		      cur_summary_lto->side_effects = true;
+		      changed = true;
+		    }
 		  if (!ignore_stores)
 		    {
 		      changed |= cur_summary_lto->stores->merge
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 482c4e4633e..49c99f263a7 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -34,6 +34,7 @@ struct GTY(()) modref_summary
   eaf_flags_t retslot_flags;
   eaf_flags_t static_chain_flags;
   bool writes_errno;
+  bool side_effects;
 
   modref_summary ();
   ~modref_summary ();
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index e5048092939..505ed4f8a3b 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -506,11 +506,10 @@ worse_state (enum pure_const_state_e *state, bool *looping,
   *looping = MAX (*looping, looping2);
 }
 
-/* Recognize special cases of builtins that are by themselves not pure or const
+/* Recognize special cases of builtins that are by themselves not const
    but function using them is.  */
-static bool
-special_builtin_state (enum pure_const_state_e *state, bool *looping,
-		       tree callee)
+bool
+builtin_safe_for_const_function_p (bool *looping, tree callee)
 {
   if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (callee))
@@ -532,11 +531,9 @@ special_builtin_state (enum pure_const_state_e *state, bool *looping,
       case BUILT_IN_DWARF_CFA:
       case BUILT_IN_RETURN_ADDRESS:
 	*looping = false;
-	*state = IPA_CONST;
 	return true;
       case BUILT_IN_PREFETCH:
 	*looping = true;
-	*state = IPA_CONST;
 	return true;
       default:
 	break;
@@ -594,17 +591,16 @@ check_call (funct_state local, gcall *call, bool ipa)
      graph.  */
   if (callee_t)
     {
-      enum pure_const_state_e call_state;
       bool call_looping;
 
       if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
 	  && !nonfreeing_call_p (call))
 	local->can_free = true;
 
-      if (special_builtin_state (&call_state, &call_looping, callee_t))
+      if (builtin_safe_for_const_function_p (&call_looping, callee_t))
 	{
 	  worse_state (&local->pure_const_state, &local->looping,
-		       call_state, call_looping,
+		       IPA_CONST, call_looping,
 		       NULL, NULL);
 	  return;
 	}
@@ -1007,6 +1003,51 @@ malloc_candidate_p (function *fun, bool ipa)
 
 #undef DUMP_AND_RETURN
 
+/* Return true if function is known to be finite.  */
+
+bool
+finite_function_p ()
+{
+  /* Const functions cannot have back edges (an
+     indication of possible infinite loop side
+     effect.  */
+  bool finite = true;
+  if (mark_dfs_back_edges ())
+    {
+      /* Preheaders are needed for SCEV to work.
+	 Simple latches and recorded exits improve chances that loop will
+	 proved to be finite in testcases such as in loop-15.c
+	 and loop-24.c  */
+      loop_optimizer_init (LOOPS_HAVE_PREHEADERS
+			   | LOOPS_HAVE_SIMPLE_LATCHES
+			   | LOOPS_HAVE_RECORDED_EXITS);
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	flow_loops_dump (dump_file, NULL, 0);
+      if (mark_irreducible_loops ())
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "    has irreducible loops\n");
+	  finite = false;
+	}
+      else
+	{
+	  scev_initialize ();
+	  for (auto loop : loops_list (cfun, 0))
+	    if (!finite_loop_p (loop))
+	      {
+		if (dump_file)
+		  fprintf (dump_file, "    cannot prove finiteness of "
+			   "loop %i\n", loop->num);
+		finite =false;
+		break;
+	      }
+	  scev_finalize ();
+	}
+      loop_optimizer_finalize ();
+    }
+  return finite;
+}
+
 /* This is the main routine for finding the reference patterns for
    global variables within a function FN.  */
 
@@ -1065,45 +1106,10 @@ analyze_function (struct cgraph_node *fn, bool ipa)
     }
 
 end:
-  if (l->pure_const_state != IPA_NEITHER)
-    {
-      /* Const functions cannot have back edges (an
-	 indication of possible infinite loop side
-	 effect.  */
-      if (mark_dfs_back_edges ())
-        {
-	  /* Preheaders are needed for SCEV to work.
-	     Simple latches and recorded exits improve chances that loop will
-	     proved to be finite in testcases such as in loop-15.c
-	     and loop-24.c  */
-	  loop_optimizer_init (LOOPS_HAVE_PREHEADERS
-			       | LOOPS_HAVE_SIMPLE_LATCHES
-			       | LOOPS_HAVE_RECORDED_EXITS);
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    flow_loops_dump (dump_file, NULL, 0);
-	  if (mark_irreducible_loops ())
-	    {
-	      if (dump_file)
-	        fprintf (dump_file, "    has irreducible loops\n");
-	      l->looping = true;
-	    }
-	  else
-	    {
-	      scev_initialize ();
-	      for (auto loop : loops_list (cfun, 0))
-		if (!finite_loop_p (loop))
-		  {
-		    if (dump_file)
-		      fprintf (dump_file, "    cannot prove finiteness of "
-			       "loop %i\n", loop->num);
-		    l->looping =true;
-		    break;
-		  }
-	      scev_finalize ();
-	    }
-          loop_optimizer_finalize ();
-	}
-    }
+  if (l->pure_const_state != IPA_NEITHER
+      && !l->looping
+      && !finite_function_p ())
+    l->looping = true;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file, "    checking previously known:");
@@ -1539,9 +1545,9 @@ propagate_pure_const (void)
 		      edge_looping = y_l->looping;
 		    }
 		}
-	      else if (special_builtin_state (&edge_state, &edge_looping,
-					      y->decl))
-		;
+	      else if (builtin_safe_for_const_function_p (&edge_looping,
+							   y->decl))
+		edge_state = IPA_CONST;
 	      else
 		state_from_flags (&edge_state, &edge_looping,
 				  flags_from_decl_or_type (y->decl),
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 3cfaf2d2737..824780f562a 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -47,6 +47,10 @@ void ipa_merge_profiles (struct cgraph_node *dst,
 			 struct cgraph_node *src, bool preserve_body = false);
 bool recursive_call_p (tree, tree);
 
+/* In ipa-pure-const.c  */
+bool finite_function_p ();
+bool builtin_safe_for_const_function_p (bool *, tree);
+
 /* In ipa-profile.c  */
 bool ipa_propagate_frequency (struct cgraph_node *node);


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-11-10 15:02 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-10 15:02 [gcc r12-5126] Extend modref by side-effect analysis Jan Hubicka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).