On September 26, 2020 10:20:20 AM GMT+02:00, Jan Hubicka <hubi...@ucw.cz> wrote:
>Hi,
>this patchs finishes the parameter tracking by implementing the
>iterative
>dataflow in propagation stage. This is necessary since we now can
>propagate how the pointers are passed around recursive calls (as done
>in
>a testcase) and thus it is no-longer safe to simply merge all summaries
>in one SCC component of the call-graph.
>
>cc1plus stats are now:
>
>Alias oracle query stats:
>  refs_may_alias_p: 62971744 disambiguations, 73160711 queries
>  ref_maybe_used_by_call_p: 141176 disambiguations, 63867883 queries
>  call_may_clobber_ref_p: 23573 disambiguations, 29322 queries
>  nonoverlapping_component_refs_p: 0 disambiguations, 37720 queries
>nonoverlapping_refs_since_match_p: 19432 disambiguations, 55659 must
>overlaps, 75860 queries
>  aliasing_component_refs_p: 54724 disambiguations, 753570 queries
>  TBAA oracle: 24124230 disambiguations 56228428 queries
>               16058141 are in alias set 0
>               10338303 queries asked about the same object
>               125 queries asked about the same alias set
>               0 access volatile
>               3919230 are dependent in the DAG
>               1788399 are aritificially in conflict with void *
>
>Modref stats:
>  modref use: 10408 disambiguations, 46993 queries
>  modref clobber: 1418549 disambiguations, 1951251 queries
>  4898707 tbaa queries (2.510547 per modref query)
>  396878 base compares (0.203397 per modref query)
>
>PTA query stats:
>  pt_solution_includes: 975364 disambiguations, 13604284 queries
>  pt_solutions_intersect: 1026606 disambiguations, 13181198 queries
>
>So compared to
>https://gcc.gnu.org/pipermail/gcc-patches/2020-September/554692.html we
>get 25%
>use disambiguations and 91% more clobber disambiguations.
>
>Tramp3d is
>
>Alias oracle query stats:
>  refs_may_alias_p: 2056905 disambiguations, 2317461 queries
>  ref_maybe_used_by_call_p: 7137 disambiguations, 2093762 queries
>  call_may_clobber_ref_p: 234 disambiguations, 234 queries
>  nonoverlapping_component_refs_p: 0 disambiguations, 4313 queries
>nonoverlapping_refs_since_match_p: 329 disambiguations, 10200 must
>overlaps, 10616 queries
>  aliasing_component_refs_p: 858 disambiguations, 34600 queries
>  TBAA oracle: 894996 disambiguations 1695991 queries
>               138346 are in alias set 0
>               470668 queries asked about the same object
>               0 queries asked about the same alias set
>               0 access volatile
>               191666 are dependent in the DAG
>               315 are aritificially in conflict with void *
>
>Modref stats:
>  modref use: 842 disambiguations, 2265 queries
>  modref clobber: 14833 disambiguations, 28900 queries
>  34884 tbaa queries (1.207059 per modref query)
>  5041 base compares (0.174429 per modref query)
>
>PTA query stats:
>  pt_solution_includes: 313372 disambiguations, 525724 queries
>  pt_solutions_intersect: 130374 disambiguations, 415138 queries
>
>So about twice many use and 40% clobber disambiguations.
>
>Bootstrapped/regtested x86_64-linux, I plan to commit it later today
>after
>more testing.

Any compile time figures for this? Firefox? 

>
>2020-09-26  Jan Hubicka  <hubi...@ucw.cz>
>
>       * ipa-inline-transform.c: Include ipa-modref-tree.h and ipa-modref.h.
>       (inline_call): Call ipa_merge_modref_summary_after_inlining.
>       * ipa-inline.c (ipa_inline): Do not free summaries.
>       * ipa-modref.c (dump_records): Fix formating.
>       (merge_call_side_effects): Break out from ...
>       (analyze_call): ... here; record recursive calls.
>       (analyze_stmt): Add new parameter RECURSIVE_CALLS.
>       (analyze_function): Do iterative dataflow on recursive calls.
>       (compute_parm_map): New function.
>       (ipa_merge_modref_summary_after_inlining): New function.
>       (collapse_loads): New function.
>       (modref_propagate_in_scc): Break out from ...
>       (pass_ipa_modref::execute): ... here; Do iterative dataflow.
>       * ipa-modref.h (ipa_merge_modref_summary_after_inlining): Declare.
>
>gcc/testsuite/ChangeLog:
>
>2020-09-26  Jan Hubicka  <hubi...@ucw.cz>
>
>       * gcc.dg/lto/modref-1_0.c: New test.
>       * gcc.dg/lto/modref-1_1.c: New test.
>       * gcc.dg/tree-ssa/modref-2.c: New test.
>
>diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
>index 5e37e612bfd..af2c2856aaa 100644
>--- a/gcc/ipa-inline-transform.c
>+++ b/gcc/ipa-inline-transform.c
>@@ -48,6 +48,8 @@ along with GCC; see the file COPYING3.  If not see
> #include "cfg.h"
> #include "basic-block.h"
> #include "ipa-utils.h"
>+#include "ipa-modref-tree.h"
>+#include "ipa-modref.h"
> 
> int ncalls_inlined;
> int nfunctions_inlined;
>@@ -487,6 +489,7 @@ inline_call (struct cgraph_edge *e, bool
>update_original,
>   gcc_assert (curr->callee->inlined_to == to);
> 
>   old_size = ipa_size_summaries->get (to)->size;
>+  ipa_merge_modref_summary_after_inlining (e);
>   ipa_merge_fn_summary_after_inlining (e);
>   if (e->in_polymorphic_cdtor)
>     mark_all_inlined_calls_cdtor (e->callee);
>diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>index c667de2a97c..225a0140725 100644
>--- a/gcc/ipa-inline.c
>+++ b/gcc/ipa-inline.c
>@@ -2770,9 +2770,6 @@ ipa_inline (void)
>       }
>     }
> 
>-  /* Free ipa-prop structures if they are no longer needed.  */
>-  ipa_free_all_structures_after_iinln ();
>-
>   if (dump_enabled_p ())
>     dump_printf (MSG_NOTE,
>                "\nInlined %i calls, eliminated %i functions\n\n",
>diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
>index 44b844b90db..385b0318442 100644
>--- a/gcc/ipa-modref.c
>+++ b/gcc/ipa-modref.c
>@@ -175,7 +175,7 @@ dump_records (modref_records *tt, FILE *out)
>         fprintf (out, "        Ref %i: alias set %i\n", (int)j, r->ref);
>         if (r->every_access)
>           {
>-            fprintf (out, "        Every access\n");
>+            fprintf (out, "          Every access\n");
>             continue;
>           }
>         size_t k;
>@@ -437,11 +437,70 @@ ignore_stores_p (tree caller, int flags)
>   return false;
> }
> 
>-/* Analyze function call STMT in function F.  */
>+/* Merge side effects of call STMT to function with CALLEE_SUMMARY
>+   int CUR_SUMMARY.  Return true if something changed.
>+   If IGNORE_STORES is true, do not merge stores.  */
>+
>+bool
>+merge_call_side_effects (modref_summary *cur_summary,
>+                       gimple *stmt, modref_summary *callee_summary,
>+                       bool ignore_stores)
>+{
>+  auto_vec <int, 32> parm_map;
>+  bool changed = false;
>+
>+  parm_map.safe_grow (gimple_call_num_args (stmt));
>+  for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
>+    {
>+      tree op = gimple_call_arg (stmt, i);
>+      STRIP_NOPS (op);
>+      if (TREE_CODE (op) == SSA_NAME
>+        && SSA_NAME_IS_DEFAULT_DEF (op)
>+        && TREE_CODE (SSA_NAME_VAR (op)) == PARM_DECL)
>+      {
>+        int index = 0;
>+        for (tree t = DECL_ARGUMENTS (current_function_decl);
>+             t != SSA_NAME_VAR (op); t = DECL_CHAIN (t))
>+          {
>+            if (!t)
>+              {
>+                index = -1;
>+                break;
>+              }
>+            index++;
>+          }
>+        parm_map[i] = index;
>+      }
>+      else if (points_to_local_or_readonly_memory_p (op))
>+      parm_map[i] = -2;
>+      else
>+      parm_map[i] = -1;
>+    }
>+
>+  /* Merge with callee's summary.  */
>+  if (cur_summary->loads)
>+    changed |= cur_summary->loads->merge (callee_summary->loads,
>&parm_map);
>+  if (cur_summary->loads_lto)
>+    changed |= cur_summary->loads_lto->merge
>(callee_summary->loads_lto,
>+                                            &parm_map);
>+  if (!ignore_stores)
>+    {
>+      if (cur_summary->stores)
>+      changed |= cur_summary->stores->merge (callee_summary->stores,
>+                                             &parm_map);
>+      if (cur_summary->stores_lto)
>+      changed |= cur_summary->stores_lto->merge
>(callee_summary->stores_lto,
>+                                                 &parm_map);
>+    }
>+  return changed;
>+}
>+
>+/* Analyze function call STMT in function F.
>+   Remember recursive calls in RECURSIVE_CALLS.  */
> 
> static bool
> analyze_call (modref_summary *cur_summary,
>-            gimple *stmt)
>+            gimple *stmt, vec <gimple *> *recursive_calls)
> {
>/* Check flags on the function call.  In certain cases, analysis can be
>      simplified.  */
>@@ -505,6 +564,7 @@ analyze_call (modref_summary *cur_summary,
>      there's nothing to do.  */
>   if (recursive_call_p (current_function_decl, callee))
>     {
>+      recursive_calls->safe_push (stmt);
>       if (dump_file)
>       fprintf (dump_file, " - Skipping recursive call.\n");
>       return true;
>@@ -550,48 +610,7 @@ analyze_call (modref_summary *cur_summary,
>       return false;
>     }
> 
>-  auto_vec <int, 32> parm_map;
>-
>-  parm_map.safe_grow (gimple_call_num_args (stmt));
>-  for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
>-    {
>-      tree op = gimple_call_arg (stmt, i);
>-      STRIP_NOPS (op);
>-      if (TREE_CODE (op) == SSA_NAME
>-        && SSA_NAME_IS_DEFAULT_DEF (op)
>-        && TREE_CODE (SSA_NAME_VAR (op)) == PARM_DECL)
>-      {
>-        int index = 0;
>-        for (tree t = DECL_ARGUMENTS (current_function_decl);
>-             t != SSA_NAME_VAR (op); t = DECL_CHAIN (t))
>-          {
>-            if (!t)
>-              {
>-                index = -1;
>-                break;
>-              }
>-            index++;
>-          }
>-        parm_map[i] = index;
>-      }
>-      else if (points_to_local_or_readonly_memory_p (op))
>-      parm_map[i] = -2;
>-      else
>-      parm_map[i] = -1;
>-    }
>-
>-  /* Merge with callee's summary.  */
>-  if (cur_summary->loads)
>-    cur_summary->loads->merge (callee_summary->loads, &parm_map);
>-  if (cur_summary->loads_lto)
>-    cur_summary->loads_lto->merge (callee_summary->loads_lto,
>&parm_map);
>-  if (!ignore_stores)
>-    {
>-      if (cur_summary->stores)
>-      cur_summary->stores->merge (callee_summary->stores, &parm_map);
>-      if (cur_summary->stores_lto)
>-      cur_summary->stores_lto->merge (callee_summary->stores_lto,
>&parm_map);
>-    }
>+  merge_call_side_effects (cur_summary, stmt, callee_summary,
>ignore_stores);
> 
>   return true;
> }
>@@ -654,7 +673,8 @@ analyze_store (gimple *, tree, tree op, void *data)
>    If IPA is true do not merge in side effects of calls.  */
> 
> static bool
>-analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
>+analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa,
>+            vec <gimple *> *recursive_calls)
> {
>   /* There is no need to record clobbers.  */
>   if (gimple_clobber_p (stmt))
>@@ -677,7 +697,7 @@ analyze_stmt (modref_summary *summary, gimple
>*stmt, bool ipa)
>      return false;
>    case GIMPLE_CALL:
>      if (!ipa)
>-       return analyze_call (summary, stmt);
>+       return analyze_call (summary, stmt, recursive_calls);
>      return true;
>    default:
>      /* Nothing to do for other types of statements.  */
>@@ -750,6 +770,7 @@ analyze_function (function *f, bool ipa)
>     }
>   summary->finished = false;
>   int ecf_flags = flags_from_decl_or_type (current_function_decl);
>+  auto_vec <gimple *, 32> recursive_calls;
> 
> /* Analyze each statement in each basic block of the function.  If the
>statement cannot be analyzed (for any reason), the entire function
>cannot
>@@ -760,7 +781,7 @@ analyze_function (function *f, bool ipa)
>       gimple_stmt_iterator si;
>      for (si = gsi_after_labels (bb); !gsi_end_p (si); gsi_next (&si))
>       {
>-        if (!analyze_stmt (summary, gsi_stmt (si), ipa)
>+        if (!analyze_stmt (summary, gsi_stmt (si), ipa, &recursive_calls)
>             || !summary->useful_p (ecf_flags))
>           {
>             cgraph_node *fnode = cgraph_node::get (current_function_decl);
>@@ -773,6 +794,34 @@ analyze_function (function *f, bool ipa)
>       }
>     }
> 
>+  /* In non-IPA mode we need to perform iterative datafow on recursive
>calls.
>+     This needs to be done after all other side effects are computed. 
>*/
>+  if (!ipa)
>+    {
>+      bool changed = true;
>+      while (changed)
>+      {
>+        changed = false;
>+        for (unsigned i = 0; i < recursive_calls.length (); i++)
>+          {
>+            changed |= merge_call_side_effects
>+                        (summary, recursive_calls[i], summary,
>+                         ignore_stores_p (current_function_decl,
>+                                          gimple_call_flags
>+                                               (recursive_calls[i])));
>+            if (!summary->useful_p (ecf_flags))
>+              {
>+                cgraph_node *fnode = cgraph_node::get (current_function_decl);
>+                summaries->remove (fnode);
>+                if (dump_file)
>+                  fprintf (dump_file,
>+                           " - modref done with result: not tracked.\n");
>+                return;
>+              }
>+          }
>+      }
>+    }
>+
>   if (!ipa)
>     summary->finished = true;
> 
>@@ -1276,71 +1325,176 @@ ignore_edge (struct cgraph_edge *e)
>            & (ECF_CONST | ECF_NOVOPS));
> }
> 
>-/* Run the IPA pass.  This will take a function's summaries and calls
>and
>-   construct new summaries which represent a transitive closure.  So
>that
>-   summary of an analyzed function contains information about the
>loads and
>-   stores that the function or any function that it calls does.  */
>+/* Compute parm_map for CALLE_EDGE.  */
> 
>-unsigned int pass_ipa_modref::execute (function *)
>+static void
>+compute_parm_map (cgraph_edge *callee_edge, vec<int> *parm_map)
>+{
>+  class ipa_edge_args *args;
>+  if (ipa_node_params_sum
>+      && !callee_edge->call_stmt_cannot_inline_p
>+      && (args = IPA_EDGE_REF (callee_edge)) != NULL)
>+    {
>+      int i, count = ipa_get_cs_argument_count (args);
>+      class ipa_node_params *caller_parms_info, *callee_pi;
>+      class ipa_call_summary *es
>+           = ipa_call_summaries->get (callee_edge);
>+      cgraph_node *callee
>+       = callee_edge->callee->function_or_virtual_thunk_symbol
>+                            (NULL, callee_edge->caller);
>+
>+      caller_parms_info = IPA_NODE_REF
>(callee_edge->caller->inlined_to
>+                                      ? callee_edge->caller->inlined_to
>+                                      : callee_edge->caller);
>+      callee_pi = IPA_NODE_REF (callee);
>+
>+      (*parm_map).safe_grow (count);
>+
>+      for (i = 0; i < count; i++)
>+      {
>+        if (es && es->param[i].points_to_local_or_readonly_memory)
>+          {
>+            (*parm_map)[i] = -2;
>+            continue;
>+          }
>+
>+        struct ipa_jump_func *jf
>+           = ipa_get_ith_jump_func (args, i);
>+        if (jf)
>+          {
>+            tree cst = ipa_value_from_jfunc (caller_parms_info,
>+                                             jf,
>+                                             ipa_get_type
>+                                               (callee_pi, i));
>+            if (cst && points_to_local_or_readonly_memory_p (cst))
>+              {
>+                (*parm_map)[i] = -2;
>+                continue;
>+              }
>+          }
>+        if (jf && jf->type == IPA_JF_PASS_THROUGH)
>+          {
>+            (*parm_map)[i]
>+               = ipa_get_jf_pass_through_formal_id (jf);
>+            continue;
>+          }
>+        if (jf && jf->type == IPA_JF_ANCESTOR)
>+          (*parm_map)[i] = ipa_get_jf_ancestor_formal_id (jf);
>+        else
>+          (*parm_map)[i] = -1;
>+      }
>+      if (dump_file)
>+      {
>+        fprintf (dump_file, "  Parm map: ");
>+        for (i = 0; i < count; i++)
>+          fprintf (dump_file, " %i", (*parm_map)[i]);
>+        fprintf (dump_file, "\n");
>+      }
>+    }
>+}
>+
>+/* Call EDGE was inlined; merge summary from callee to the caller.  */
>+
>+void
>+ipa_merge_modref_summary_after_inlining (cgraph_edge *edge)
> {
>   if (!summaries)
>-    return 0;
>+    return;
> 
>-  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
>-                                       symtab->cgraph_count);
>-  int order_pos;
>-  order_pos = ipa_reduced_postorder (order, true, ignore_edge);
>-  int i;
>+  struct cgraph_node *to = (edge->caller->inlined_to
>+                          ? edge->caller->inlined_to : edge->caller);
>+  class modref_summary *to_info = summaries->get (to);
> 
>-  /* Iterate over all strongly connected components in post-order.  */
>-  for (i = 0; i < order_pos; i++)
>+  if (!to_info)
>+    return;
>+
>+  class modref_summary *callee_info = summaries->get (edge->callee);
>+  int flags = flags_from_decl_or_type (edge->callee->decl);
>+
>+  if (!callee_info)
>     {
>-      bool its_hopeless = false;
>-      modref_records *loads = NULL;
>-      modref_records *stores = NULL;
>-      modref_records_lto *loads_lto = NULL;
>-      modref_records_lto *stores_lto = NULL;
>+      if (ignore_stores_p (edge->callee->decl, flags))
>+      {
>+        if (to_info->loads)
>+          to_info->loads->collapse ();
>+        if (to_info->loads_lto)
>+          to_info->loads_lto->collapse ();
>+      }
>+      else
>+      {
>+        summaries->remove (to);
>+        summaries->remove (edge->callee);
>+        return;
>+      }
>+    }
>+  else
>+    {
>+      auto_vec <int, 32> parm_map+
>+      compute_parm_map (edge, &parm_map);
>+
>+      if (to_info->loads)
>+      to_info->loads->merge (callee_info->loads, &parm_map);
>+      if (to_info->stores)
>+      to_info->stores->merge (callee_info->stores, &parm_map);
>+      if (to_info->loads_lto)
>+      to_info->loads_lto->merge (callee_info->loads_lto, &parm_map);
>+      if (to_info->stores_lto)
>+      to_info->stores_lto->merge (callee_info->stores_lto, &parm_map);
>+    }
>+  if (!to_info->useful_p (flags))
>+    summaries->remove (to);
>+  summaries->remove (edge->callee);
>+  return;
>+}
> 
>-      /* Get the component's representative.  That's just any node in
>the
>-       component from which we can traverse the entire component.  */
>-      struct cgraph_node *component_node = order[i];
>-      cgraph_node *first = NULL;
>+/* Collapse loads and return true if something changed.  */
> 
>-      if (dump_file)
>-      fprintf (dump_file, "Start of SCC component\n");
>+bool
>+collapse_loads (modref_summary *cur_summary)
>+{
>+  bool changed = false;
>+
>+  if (cur_summary->loads && !cur_summary->loads->every_base)
>+    {
>+      cur_summary->loads->collapse ();
>+      changed = true;
>+    }
>+  if (cur_summary->loads_lto
>+      && !cur_summary->loads_lto->every_base)
>+    {
>+      cur_summary->loads_lto->collapse ();
>+      changed = true;
>+    }
>+  return changed;
>+}
> 
>-      /* Walk the component.  CUR is the current node of the component
>that's
>-       being processed.  */
>-      for (struct cgraph_node *cur = component_node; cur &&
>!its_hopeless;
>+/* Perform iterative dataflow on SCC component starting in
>COMPONENT_NODE.  */
>+
>+static void
>+modref_propagate_in_scc (cgraph_node *component_node)
>+{
>+  bool changed = true;
>+  int iteration = 0;
>+
>+  while (changed)
>+    {
>+      changed = false;
>+      for (struct cgraph_node *cur = component_node; cur;
>          cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
>       {
>-        /* Merge in summaries from CUR.  */
>-        modref_summary *cur_summary = summaries->get (cur);
>-
>-        if (dump_file)
>-          fprintf (dump_file, "  Processing %s\n",
>-                   cur->dump_name ());
>+        cgraph_node *node = cur->inlined_to ? cur->inlined_to : cur;
>+        modref_summary *cur_summary = summaries->get (node);
> 
>-        /* We don't know anything about CUR, hence we cannot tell anything
>-           about the entire component.  */
>         if (!cur_summary)
>-          {
>-            if (dump_file)
>-              fprintf (dump_file, "    No summary\n");
>-            its_hopeless = true;
>-            break;
>-          }
>+          continue;
>+
>+        if (dump_file)
>+          fprintf (dump_file, "  Processing %s%s%s\n",
>+                   cur->dump_name (),
>+                   TREE_READONLY (cur->decl) ? " (const)" : "",
>+                   DECL_PURE_P (cur->decl) ? " (pure)" : "");
> 
>-        /* Summaries are all going to be same, pick first ones and merge
>-           everything in.  */
>-        if (!first)
>-          {
>-            first = cur;
>-            loads = cur_summary->loads;
>-            stores = cur_summary->stores;
>-            loads_lto = cur_summary->loads_lto;
>-            stores_lto = cur_summary->stores_lto;
>-          }
>         for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
>           {
>             if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS))
>@@ -1350,20 +1504,22 @@ unsigned int pass_ipa_modref::execute (function
>*)
>                 if (dump_file)
>                   fprintf (dump_file, "    Indirect call: "
>                            "collapsing loads\n");
>-                if (loads)
>-                  loads->collapse ();
>-                if (loads_lto)
>-                  loads_lto->collapse ();
>+                changed |= collapse_loads (cur_summary);
>               }
>             else
>               {
>                 if (dump_file)
>                   fprintf (dump_file, "    Indirect call: giving up\n");
>-                its_hopeless = true;
>+                summaries->remove (node);
>+                changed = true;
>+                cur_summary = NULL;
>+                break;
>               }
>           }
> 
>-        /* Walk every function that CUR calls and merge its summary.  */
>+        if (!cur_summary)
>+          continue;
>+
>         for (cgraph_edge *callee_edge = cur->callees; callee_edge;
>              callee_edge = callee_edge->next_callee)
>           {
>@@ -1371,42 +1527,26 @@ unsigned int pass_ipa_modref::execute (function
>*)
>             modref_summary *callee_summary;
>             struct cgraph_node *callee;
> 
>-            if (flags & (ECF_CONST | ECF_NOVOPS))
>+            if (flags & (ECF_CONST | ECF_NOVOPS)
>+                || !callee_edge->inline_failed)
>               continue;
> 
>-            if (dump_file)
>-              fprintf (dump_file, "    Call to %s\n",
>-                       callee_edge->callee->dump_name ());
>-
>-            /* We can not safely optimize based on summary of callee if it
>-               does not always bind to current def: it is possible that
>-               memory load was optimized out earlier which may not happen in
>-               the interposed variant.  */
>-            if (!callee_edge->binds_to_current_def_p ())
>-              {
>-                if (loads)
>-                  loads->collapse ();
>-                if (loads_lto)
>-                  loads_lto->collapse ();
>-                if (dump_file)
>-                  fprintf (dump_file, "      May not bind local;"
>-                           " collapsing loads\n");
>-              }
>-
>             /* Get the callee and its summary.  */
>             enum availability avail;
>             callee = callee_edge->callee->function_or_virtual_thunk_symbol
>                        (&avail, cur);
> 
>-            /* See if we can derive something from ECF flags.  Be careful
>on
>-               not skipping calls within the SCC component:  we must merge
>-               all their summaries.
>-               If we switch to iterative dataflow that may be necessary
>-               for future improvements this may go away.  */
>-            if (callee->aux
>-                && ((struct ipa_dfs_info *)cur->aux)->scc_no
>-                   == ((struct ipa_dfs_info *)callee->aux)->scc_no)
>-              flags = 0;
>+            /* It is not necessary to re-process calls outside of the
>+               SCC component.  */
>+            if (iteration > 0
>+                && (!callee->aux
>+                    || ((struct ipa_dfs_info *)cur->aux)->scc_no
>+                        != ((struct ipa_dfs_info *)callee->aux)->scc_no))
>+              continue;
>+
>+            if (dump_file)
>+              fprintf (dump_file, "    Call to %s\n",
>+                       callee_edge->callee->dump_name ());
> 
>             bool ignore_stores = ignore_stores_p (cur->decl, flags);
> 
>@@ -1418,101 +1558,128 @@ unsigned int pass_ipa_modref::execute
>(function *)
>               {
>                 if (!ignore_stores)
>                   {
>-                    its_hopeless = true;
>                     if (dump_file && avail <= AVAIL_INTERPOSABLE)
>                       fprintf (dump_file, "      Call target interposable"
>                                " or not available\n");
>                     else if (dump_file)
>                       fprintf (dump_file, "      No call target summary\n");
>+
>+                    summaries->remove (node);
>+                    changed = true;
>                     break;
>                   }
>                 else
>                   {
>-                    if (loads)
>-                      loads->collapse ();
>-                    if (loads_lto)
>-                      loads_lto->collapse ();
>                     if (dump_file && avail <= AVAIL_INTERPOSABLE)
>                       fprintf (dump_file, "      Call target interposable"
>-                               "or not available; collapsing loads\n");
>+                               " or not available; collapsing loads\n");
>                     else if (dump_file)
>                       fprintf (dump_file, "      No call target summary;"
>                                " collapsing loads\n");
>+
>+                    changed |= collapse_loads (cur_summary);
>                     continue;
>                   }
>               }
> 
>+            /* We can not safely optimize based on summary of callee if it
>+               does not always bind to current def: it is possible that
>+               memory load was optimized out earlier which may not happen in
>+               the interposed variant.  */
>+            if (!callee_edge->binds_to_current_def_p ())
>+              {
>+                changed |= collapse_loads (cur_summary);
>+                if (dump_file)
>+                  fprintf (dump_file, "      May not bind local;"
>+                           " collapsing loads\n");
>+              }
>+
>+
>             auto_vec <int, 32> parm_map;
>-            /* TODO: compute parm_map.  */
>+
>+            compute_parm_map (callee_edge, &parm_map);
> 
>             /* Merge in callee's information.  */
>-            if (callee_summary->loads
>-                && callee_summary->loads != loads)
>-              loads->merge (callee_summary->loads, &parm_map);
>-            if (callee_summary->stores
>-                && callee_summary->stores != stores)
>-              stores->merge (callee_summary->stores, &parm_map);
>-            if (callee_summary->loads_lto
>-                && callee_summary->loads_lto != loads_lto)
>-              loads_lto->merge (callee_summary->loads_lto, &parm_map);
>-            if (callee_summary->stores_lto
>-                && callee_summary->stores_lto != stores_lto)
>-              stores_lto->merge (callee_summary->stores_lto, &parm_map);
>+            if (callee_summary->loads)
>+              changed |= cur_summary->loads->merge
>+                              (callee_summary->loads, &parm_map);
>+            if (callee_summary->stores)
>+              changed |= cur_summary->stores->merge
>+                              (callee_summary->stores, &parm_map);
>+            if (callee_summary->loads_lto)
>+              changed |= cur_summary->loads_lto->merge
>+                              (callee_summary->loads_lto, &parm_map);
>+            if (callee_summary->stores_lto)
>+              changed |= cur_summary->stores_lto->merge
>+                              (callee_summary->stores_lto, &parm_map);
>+            if (dump_file && changed)
>+              cur_summary->dump (dump_file);
>           }
>       }
>-
>-      /* At this time, ipa_loads and ipa_stores contain information
>-         about all loads and stores done by any of the component's nodes
>and
>-         all functions that any of the nodes calls.  We will now propagate
>-         this information to all nodes in the component.  Therefore, we
>will
>-         walk the component one more time to do it.  */
>-      for (struct cgraph_node *cur = component_node; cur;
>+      iteration++;
>+    }
>+  for (struct cgraph_node *cur = component_node; cur;
>+       cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
>+    {
>+      modref_summary *cur_summary = summaries->get (cur);
>+      if (cur_summary)
>+      cur_summary->finished = true;
>+    }
>+  if (dump_file)
>+    {
>+      fprintf (dump_file,
>+             "Propagation finished in %i iterations\n", iteration);
>+      for (struct cgraph_node *cur = component_node; cur;
>          cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
>-      {
>-        modref_summary *cur_summary = summaries->get (cur);
>-        if (!cur_summary)
>-          {
>-            /* The function doesn't have a summary.  We must have noticed
>-               that during the first pass and the hopeless flag must
>-               therefore be set.  Skip the function.  */
>-            gcc_assert (its_hopeless);
>-          }
>-        else if (its_hopeless)
>-          {
>-            if (dump_file)
>-              fprintf (dump_file, "Cleared modref info for %s\n",
>-                       cur->dump_name ());
>-            summaries->remove (cur);
>-          }
>-        else
>-          {
>-            if (cur == first)
>-              ;
>-            else
>-              {
>-                if (loads)
>-                  cur_summary->loads->merge (loads, NULL);
>-                if (stores)
>-                  cur_summary->stores->merge (stores, NULL);
>-                if (loads_lto)
>-                  cur_summary->loads_lto->merge (loads_lto, NULL);
>-                if (stores_lto)
>-                  cur_summary->stores_lto->merge (stores_lto, NULL);
>-              }
>-            cur_summary->finished = true;
>-            if (dump_file)
>-              {
>-                fprintf (dump_file, "Propagated modref for %s%s%s\n",
>-                         cur->dump_name (),
>-                         TREE_READONLY (cur->decl) ? " (const)" : "",
>-                         DECL_PURE_P (cur->decl) ? " (pure)" : "");
>-                cur_summary->dump (dump_file);
>-              }
>-          }
>-      }
>+      if (!cur->inlined_to)
>+        {
>+          modref_summary *cur_summary = summaries->get (cur);
>+
>+          fprintf (dump_file, "Propagated modref for %s%s%s\n",
>+                   cur->dump_name (),
>+                   TREE_READONLY (cur->decl) ? " (const)" : "",
>+                   DECL_PURE_P (cur->decl) ? " (pure)" : "");
>+          if (cur_summary)
>+            cur_summary->dump (dump_file);
>+          else
>+            fprintf (dump_file, "  Not tracked\n");
>+        }
>+   }
>+}
>+
>+/* Run the IPA pass.  This will take a function's summaries and calls
>and
>+   construct new summaries which represent a transitive closure.  So
>that
>+   summary of an analyzed function contains information about the
>loads and
>+   stores that the function or any function that it calls does.  */
>+
>+unsigned int
>+pass_ipa_modref::execute (function *)
>+{
>+  if (!summaries)
>+    return 0;
>+
>+  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
>+                                       symtab->cgraph_count);
>+  int order_pos;
>+  order_pos = ipa_reduced_postorder (order, true, ignore_edge);
>+  int i;
>+
>+  /* Iterate over all strongly connected components in post-order.  */
>+  for (i = 0; i < order_pos; i++)
>+    {
>+      /* Get the component's representative.  That's just any node in
>the
>+       component from which we can traverse the entire component.  */
>+      struct cgraph_node *component_node = order[i];
>+
>+      if (dump_file)
>+      fprintf (dump_file, "\n\nStart of SCC component\n");
>+
>+      modref_propagate_in_scc (component_node);
>     }
>   ((modref_summaries *)summaries)->ipa = false;
>   ipa_free_postorder_info ();
>+  /* Free ipa-prop structures if they are no longer needed.  */
>+  ipa_free_all_structures_after_iinln ();
>   return 0;
> }
> 
>diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
>index 152e7154aed..b6621b498f0 100644
>--- a/gcc/ipa-modref.h
>+++ b/gcc/ipa-modref.h
>@@ -47,5 +47,6 @@ struct GTY(()) modref_summary
> 
> modref_summary *get_modref_function_summary (cgraph_node *func);
> void ipa_modref_c_finalize ();
>+void ipa_merge_modref_summary_after_inlining (cgraph_edge *e);
> 
> #endif
>diff --git a/gcc/testsuite/gcc.dg/lto/modref-1_0.c
>b/gcc/testsuite/gcc.dg/lto/modref-1_0.c
>new file mode 100644
>index 00000000000..8fcb9ec52f1
>--- /dev/null
>+++ b/gcc/testsuite/gcc.dg/lto/modref-1_0.c
>@@ -0,0 +1,14 @@
>+/* { dg-lto-do run } */
>+/* { dg-lto-options {"-O2 -flto-partition=max -flto"}  } */
>+extern void recursive (int *a, int *b, int *c, int level);
>+int
>+main()
>+{
>+  int x = 123, y=124, z=125;
>+  recursive (&x,&y,&z,1);
>+  if (y)
>+    __builtin_abort ();
>+  if (!__builtin_constant_p (z))
>+    __builtin_abort ();
>+  return 0;
>+}
>diff --git a/gcc/testsuite/gcc.dg/lto/modref-1_1.c
>b/gcc/testsuite/gcc.dg/lto/modref-1_1.c
>new file mode 100644
>index 00000000000..c7c0eaec971
>--- /dev/null
>+++ b/gcc/testsuite/gcc.dg/lto/modref-1_1.c
>@@ -0,0 +1,13 @@
>+short aa;
>+void
>+__attribute__ ((noinline, noclone))
>+recursive (int *a, int *b, int *c, int level)
>+{
>+  if (level && c)
>+    {
>+      recursive (b,a,c,0);
>+      aa++;
>+    }
>+  else
>+    *a=0;
>+}
>diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c
>b/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c
>new file mode 100644
>index 00000000000..9999d37369d
>--- /dev/null
>+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c
>@@ -0,0 +1,26 @@
>+/* { dg-do run } */
>+/* { dg-options "-O2"  } */
>+short aa;
>+void
>+__attribute__ ((noinline, noclone))
>+recursive (int *a, int *b, int *c, int level)
>+{
>+  if (level && c)
>+    {
>+      recursive (b,a,c,0);
>+      aa++;
>+    }
>+  else
>+    *a=0;
>+}
>+int
>+main()
>+{
>+  int x = 123, y=124, z=125;
>+  recursive (&x,&y,&z,1);
>+  if (y)
>+    __builtin_abort ();
>+  if (!__builtin_constant_p (z))
>+    __builtin_abort ();
>+  return 0;
>+}

Reply via email to