ipa-cp: Do not be too optimistic about self-recursive edges (PR 107661)

Message ID ri6edtvulho.fsf@suse.cz
State New
Headers
Series ipa-cp: Do not be too optimistic about self-recursive edges (PR 107661) |

Commit Message

Martin Jambor Nov. 22, 2022, 2:12 p.m. UTC
  Hi,

PR 107661 shows that function push_agg_values_for_index_from_edge
should not attempt to optimize self-recursive call graph edges when
called from cgraph_edge_brings_all_agg_vals_for_node.  Unlike when
being called from find_aggregate_values_for_callers_subset, we cannot
expect that any cloning for constants would lead to the edge leading
from a new clone to the same new clone, in this case it would only be
redirected to a new callee.

Fixed by adding a parameter to push_agg_values_from_edge whether being
optimistic about self-recursive edges is possible.

Bootstrapped, LTO-bootstrapped and tested on x86_64-linux.  OK for
trunk?

Thanks,

Martin


gcc/ChangeLog:

2022-11-22  Martin Jambor  <mjambor@suse.cz>

	PR ipa/107661
	* ipa-cp.cc (push_agg_values_from_edge): New parameter
	optimize_self_recursion, use it to decide whether to pass interim to
	the helper function.
	(find_aggregate_values_for_callers_subset): Pass true in the new
	parameter of push_agg_values_from_edge.
	(cgraph_edge_brings_all_agg_vals_for_node): Pass false in the new
	parameter of push_agg_values_from_edge.

gcc/testsuite/ChangeLog:

2022-11-22  Martin Jambor  <mjambor@suse.cz>

	PR ipa/107661
	* g++.dg/ipa/pr107661.C: New test.
---
 gcc/ipa-cp.cc                       | 18 +++++++-----
 gcc/testsuite/g++.dg/ipa/pr107661.C | 45 +++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ipa/pr107661.C
  

Comments

Jan Hubicka Nov. 22, 2022, 2:20 p.m. UTC | #1
> Hi,
> 
> PR 107661 shows that function push_agg_values_for_index_from_edge
> should not attempt to optimize self-recursive call graph edges when
> called from cgraph_edge_brings_all_agg_vals_for_node.  Unlike when
> being called from find_aggregate_values_for_callers_subset, we cannot
> expect that any cloning for constants would lead to the edge leading
> from a new clone to the same new clone, in this case it would only be
> redirected to a new callee.
> 
> Fixed by adding a parameter to push_agg_values_from_edge whether being
> optimistic about self-recursive edges is possible.
> 
> Bootstrapped, LTO-bootstrapped and tested on x86_64-linux.  OK for
> trunk?
OK,
thanks!
Honya
> 
> Thanks,
> 
> Martin
> 
> 
> gcc/ChangeLog:
> 
> 2022-11-22  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR ipa/107661
> 	* ipa-cp.cc (push_agg_values_from_edge): New parameter
> 	optimize_self_recursion, use it to decide whether to pass interim to
> 	the helper function.
> 	(find_aggregate_values_for_callers_subset): Pass true in the new
> 	parameter of push_agg_values_from_edge.
> 	(cgraph_edge_brings_all_agg_vals_for_node): Pass false in the new
> 	parameter of push_agg_values_from_edge.
> 
> gcc/testsuite/ChangeLog:
> 
> 2022-11-22  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR ipa/107661
> 	* g++.dg/ipa/pr107661.C: New test.
> ---
>  gcc/ipa-cp.cc                       | 18 +++++++-----
>  gcc/testsuite/g++.dg/ipa/pr107661.C | 45 +++++++++++++++++++++++++++++
>  2 files changed, 56 insertions(+), 7 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ipa/pr107661.C
> 
> diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
> index d2bcd5e5e69..f0feb4beb8f 100644
> --- a/gcc/ipa-cp.cc
> +++ b/gcc/ipa-cp.cc
> @@ -5752,14 +5752,16 @@ push_agg_values_for_index_from_edge (struct cgraph_edge *cs, int index,
>     description of ultimate callee of CS or the one it was cloned from (the
>     summary where lattices are).  If INTERIM is non-NULL, it contains the
>     current interim state of collected aggregate values which can be used to
> -   compute values passed over self-recursive edges and to skip values which
> -   clearly will not be part of intersection with INTERIM.  */
> +   compute values passed over self-recursive edges (if OPTIMIZE_SELF_RECURSION
> +   is true) and to skip values which clearly will not be part of intersection
> +   with INTERIM.  */
>  
>  static void
>  push_agg_values_from_edge (struct cgraph_edge *cs,
>  			   ipa_node_params *dest_info,
>  			   vec<ipa_argagg_value> *res,
> -			   const ipa_argagg_value_list *interim)
> +			   const ipa_argagg_value_list *interim,
> +			   bool optimize_self_recursion)
>  {
>    ipa_edge_args *args = ipa_edge_args_sum->get (cs);
>    if (!args)
> @@ -5785,7 +5787,9 @@ push_agg_values_from_edge (struct cgraph_edge *cs,
>        ipcp_param_lattices *plats = ipa_get_parm_lattices (dest_info, index);
>        if (plats->aggs_bottom)
>  	continue;
> -      push_agg_values_for_index_from_edge (cs, index, res, interim);
> +      push_agg_values_for_index_from_edge (cs, index, res,
> +					   optimize_self_recursion ? interim
> +					   : NULL);
>      }
>  }
>  
> @@ -5804,7 +5808,7 @@ find_aggregate_values_for_callers_subset (struct cgraph_node *node,
>    /* gather_edges_for_value puts a non-recursive call into the first element of
>       callers if it can.  */
>    auto_vec<ipa_argagg_value, 32> interim;
> -  push_agg_values_from_edge (callers[0], dest_info, &interim, NULL);
> +  push_agg_values_from_edge (callers[0], dest_info, &interim, NULL, true);
>  
>    unsigned valid_entries = interim.length ();
>    if (!valid_entries)
> @@ -5815,7 +5819,7 @@ find_aggregate_values_for_callers_subset (struct cgraph_node *node,
>      {
>        auto_vec<ipa_argagg_value, 32> last;
>        ipa_argagg_value_list avs (&interim);
> -      push_agg_values_from_edge (callers[i], dest_info, &last, &avs);
> +      push_agg_values_from_edge (callers[i], dest_info, &last, &avs, true);
>  
>        valid_entries = intersect_argaggs_with (interim, last);
>        if (!valid_entries)
> @@ -5882,7 +5886,7 @@ cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
>    ipa_node_params *dest_info = ipa_node_params_sum->get (node);
>    gcc_checking_assert (dest_info->ipcp_orig_node);
>    dest_info = ipa_node_params_sum->get (dest_info->ipcp_orig_node);
> -  push_agg_values_from_edge (cs, dest_info, &edge_values, &existing);
> +  push_agg_values_from_edge (cs, dest_info, &edge_values, &existing, false);
>    const ipa_argagg_value_list avl (&edge_values);
>    return avl.superset_of_p (existing);
>  }
> diff --git a/gcc/testsuite/g++.dg/ipa/pr107661.C b/gcc/testsuite/g++.dg/ipa/pr107661.C
> new file mode 100644
> index 00000000000..cc6f8538dbf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ipa/pr107661.C
> @@ -0,0 +1,45 @@
> +/* { dg-do run  { target c++11 } } */
> +/* { dg-options "-O1 -fipa-cp -fipa-cp-clone" } */
> +
> +struct R {} RGood;
> +struct L {} LBad;
> +
> +volatile int vi;
> +static void __attribute__((noipa)) L_run(void) { vi = 0; __builtin_abort (); }
> +static void callback_fn_L(void) { vi = 1; L_run(); }
> +static void callback_fn_R(void) { vi = 2; }
> +
> +struct function_ref {
> +  void (*callback)(void) = nullptr;
> +
> +  function_ref(L * pl) { callback = callback_fn_L; }
> +  function_ref(R * pr) { callback = callback_fn_R; }
> +};
> +
> +// allow one level of recursion to call callback twice
> +static int is_recur(void) {
> +    static int n = 0;
> +    switch (n++) {
> +      case 0: return 1;
> +      default: return 0;
> +    }
> +}
> +
> +static void do3(volatile int * punused, function_ref Expired) {
> +  Expired.callback();
> +
> +  if (is_recur())
> +      do3(punused, Expired);
> +}
> +
> +static void do1(function_ref Expired) {
> +  volatile int unused = 42;
> +
> +  do3(&unused, Expired);
> +}
> +
> +int main(int, const char **) { do1(&RGood); return 0; }
> +
> +void seemingly_unused_foo(void) { do1(&LBad); }
> +
> +void (*fnptr)(void) = seemingly_unused_foo;
> -- 
> 2.38.1
>
  

Patch

diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
index d2bcd5e5e69..f0feb4beb8f 100644
--- a/gcc/ipa-cp.cc
+++ b/gcc/ipa-cp.cc
@@ -5752,14 +5752,16 @@  push_agg_values_for_index_from_edge (struct cgraph_edge *cs, int index,
    description of ultimate callee of CS or the one it was cloned from (the
    summary where lattices are).  If INTERIM is non-NULL, it contains the
    current interim state of collected aggregate values which can be used to
-   compute values passed over self-recursive edges and to skip values which
-   clearly will not be part of intersection with INTERIM.  */
+   compute values passed over self-recursive edges (if OPTIMIZE_SELF_RECURSION
+   is true) and to skip values which clearly will not be part of intersection
+   with INTERIM.  */
 
 static void
 push_agg_values_from_edge (struct cgraph_edge *cs,
 			   ipa_node_params *dest_info,
 			   vec<ipa_argagg_value> *res,
-			   const ipa_argagg_value_list *interim)
+			   const ipa_argagg_value_list *interim,
+			   bool optimize_self_recursion)
 {
   ipa_edge_args *args = ipa_edge_args_sum->get (cs);
   if (!args)
@@ -5785,7 +5787,9 @@  push_agg_values_from_edge (struct cgraph_edge *cs,
       ipcp_param_lattices *plats = ipa_get_parm_lattices (dest_info, index);
       if (plats->aggs_bottom)
 	continue;
-      push_agg_values_for_index_from_edge (cs, index, res, interim);
+      push_agg_values_for_index_from_edge (cs, index, res,
+					   optimize_self_recursion ? interim
+					   : NULL);
     }
 }
 
@@ -5804,7 +5808,7 @@  find_aggregate_values_for_callers_subset (struct cgraph_node *node,
   /* gather_edges_for_value puts a non-recursive call into the first element of
      callers if it can.  */
   auto_vec<ipa_argagg_value, 32> interim;
-  push_agg_values_from_edge (callers[0], dest_info, &interim, NULL);
+  push_agg_values_from_edge (callers[0], dest_info, &interim, NULL, true);
 
   unsigned valid_entries = interim.length ();
   if (!valid_entries)
@@ -5815,7 +5819,7 @@  find_aggregate_values_for_callers_subset (struct cgraph_node *node,
     {
       auto_vec<ipa_argagg_value, 32> last;
       ipa_argagg_value_list avs (&interim);
-      push_agg_values_from_edge (callers[i], dest_info, &last, &avs);
+      push_agg_values_from_edge (callers[i], dest_info, &last, &avs, true);
 
       valid_entries = intersect_argaggs_with (interim, last);
       if (!valid_entries)
@@ -5882,7 +5886,7 @@  cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
   ipa_node_params *dest_info = ipa_node_params_sum->get (node);
   gcc_checking_assert (dest_info->ipcp_orig_node);
   dest_info = ipa_node_params_sum->get (dest_info->ipcp_orig_node);
-  push_agg_values_from_edge (cs, dest_info, &edge_values, &existing);
+  push_agg_values_from_edge (cs, dest_info, &edge_values, &existing, false);
   const ipa_argagg_value_list avl (&edge_values);
   return avl.superset_of_p (existing);
 }
diff --git a/gcc/testsuite/g++.dg/ipa/pr107661.C b/gcc/testsuite/g++.dg/ipa/pr107661.C
new file mode 100644
index 00000000000..cc6f8538dbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/pr107661.C
@@ -0,0 +1,45 @@ 
+/* { dg-do run  { target c++11 } } */
+/* { dg-options "-O1 -fipa-cp -fipa-cp-clone" } */
+
+struct R {} RGood;
+struct L {} LBad;
+
+volatile int vi;
+static void __attribute__((noipa)) L_run(void) { vi = 0; __builtin_abort (); }
+static void callback_fn_L(void) { vi = 1; L_run(); }
+static void callback_fn_R(void) { vi = 2; }
+
+struct function_ref {
+  void (*callback)(void) = nullptr;
+
+  function_ref(L * pl) { callback = callback_fn_L; }
+  function_ref(R * pr) { callback = callback_fn_R; }
+};
+
+// allow one level of recursion to call callback twice
+static int is_recur(void) {
+    static int n = 0;
+    switch (n++) {
+      case 0: return 1;
+      default: return 0;
+    }
+}
+
+static void do3(volatile int * punused, function_ref Expired) {
+  Expired.callback();
+
+  if (is_recur())
+      do3(punused, Expired);
+}
+
+static void do1(function_ref Expired) {
+  volatile int unused = 42;
+
+  do3(&unused, Expired);
+}
+
+int main(int, const char **) { do1(&RGood); return 0; }
+
+void seemingly_unused_foo(void) { do1(&LBad); }
+
+void (*fnptr)(void) = seemingly_unused_foo;