Hi,
this patch makes 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 --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 22efc06c583..d14f9e52f62 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 {
@@ -273,7 +274,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)
 {
 }
 
@@ -371,6 +372,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 ();
@@ -382,7 +384,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)
 {
 }
 
@@ -615,6 +617,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++)
@@ -647,6 +651,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++)
@@ -980,6 +986,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;
 }
 
@@ -1075,6 +1087,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))
@@ -1212,6 +1236,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;
@@ -1222,6 +1250,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)
@@ -1268,6 +1310,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;
 
@@ -1296,6 +1350,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;
 
@@ -1332,6 +1398,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)))
@@ -1363,7 +1438,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;
    }
 }
@@ -2606,6 +2688,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)
     {
@@ -2620,6 +2703,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,
@@ -2690,6 +2774,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);
 
@@ -2886,6 +2976,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;
@@ -2913,6 +3004,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;
@@ -3241,6 +3333,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;
@@ -3310,9 +3403,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));
@@ -3357,6 +3456,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)
@@ -3505,7 +3611,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);
@@ -3844,6 +3950,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))
     {
@@ -4108,6 +4247,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
@@ -4126,6 +4271,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 20170a65ded..0c3c81d1d43 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);
 

Reply via email to