Hello,

and ping please.

Martin


On Wed, Jun 03 2026, Martin Jambor wrote:
> Hi,
>
> IPA-CP contains special logic to create specially marked clones with
> constants that come from all callees (that are constant in all
> contexts).  In the past that was the only case in which the heuristics
> tried to assess the effect of multiple constants at once (that is no
> longer true since GCC 16) and they are also useful when we realize we
> can refine them still further in the case of recursive calls (such as
> in testsuite/gcc.dg/ipa/pr93707.c) turn to carry more constants after
> the initial "all-context" clone is created.
>
> Unfortunately, the code creating the clones simply redirects all
> incoming call graph edges which may include those which were indirect
> in the phase of the algorithm which gathered IPA-CP lattices and the
> arguments in these calls might contain different values than all
> initially known ones, leading to miscompilations like the one in
> PR125207.
>
> Since GCC 16 already feeds the heuristics with all known constants
> that are passed along with the constant the evaluation was initiated
> for, the all-context nodes are really only necessary for the recursive
> refinement cases described above.  It seems appropriate to only create
> them when they are what they are called, when there can be no indirect
> or unknown calls to them, i.e. when they are local.  This has the nice
> effect of simplifying decide_whether_version_node somewhat.
>
> There is some testsuite fallout.  First, IPA-CP will now no longer
> clone a non-static function to only remove a (non-constant) parameter.
> This likely only makes sense with tiny values of param
> ipa-cp-eval-threshold or very small functions which are likely going
> to be inlined anyway.  But gcc.dg/vla-1.c expected that to happen so I
> made the function to be cloned static.
>
> The testcase libgomp.c/ipcp-cb-spec1.c stopped working for the same
> reason (the outlined task body has its address taken) but looking into
> the dumps the test does not seem to test what it is supposed to, the
> test function was completely eliminated before IPA.  I have therefore
> changed it to really propagate constants.
>
> Bootstrapped and tested on x86_64-linux.  LTO-O3 bootstrap underway.  OK
> for master and the gcc-16 branch if it passes?
>
> Thanks,
>
> Martin
>
>
> gcc/ChangeLog:
>
> 2026-06-02  Martin Jambor  <[email protected]>
>
>       PR ipa/125207
>       * ipa-cp.cc (decide_about_value): Add detailed dumping about skipping
>       a value when there are no edges for it.  Make dumping about skipping
>       because of growth limit violation consistent with other cases.
>       (decide_whether_version_node): Only create all-context nodes for local
>       nodes, remove the logic doing it in other cases, allow cloning for
>       single constant lattices for non-local nodes.  Adjust dumping.
>
> gcc/testsuite/ChangeLog:
>
> 2026-06-02  Martin Jambor  <[email protected]>
>
>       PR ipa/125207
>       * gcc.dg/ipa/pr125207.c: New test.
>       * gcc.dg/vla-1.c (f1): Make function f1 static.
>
> libgomp/ChangeLog:
>
> 2026-06-03  Martin Jambor  <[email protected]>
>
>       PR ipa/125207
>       * testsuite/libgomp.c/ipcp-cb-spec1.c (test): Rework.
> ---
>  gcc/ipa-cp.cc                               | 102 ++++++++------------
>  gcc/testsuite/gcc.dg/ipa/pr125207.c         |  43 +++++++++
>  gcc/testsuite/gcc.dg/vla-1.c                |   2 +-
>  libgomp/testsuite/libgomp.c/ipcp-cb-spec1.c |  14 ++-
>  4 files changed, 93 insertions(+), 68 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/pr125207.c
>
> diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
> index 9e0b698b8cb..532bc007a2d 100644
> --- a/gcc/ipa-cp.cc
> +++ b/gcc/ipa-cp.cc
> @@ -5957,7 +5957,7 @@ decide_about_value (struct cgraph_node *node, int 
> index, HOST_WIDE_INT offset,
>    else if (val->local_size_cost + overall_size > get_max_overall_size (node))
>      {
>        if (dump_file && (dump_flags & TDF_DETAILS))
> -     fprintf (dump_file, "   Ignoring candidate value because "
> +     fprintf (dump_file, " - ignoring candidate value because "
>                "maximum unit size would be reached with %li.\n",
>                val->local_size_cost + overall_size);
>        return false;
> @@ -5965,7 +5965,19 @@ decide_about_value (struct cgraph_node *node, int 
> index, HOST_WIDE_INT offset,
>    else if (!get_info_about_necessary_edges (val, node, &freq_sum, 
> &caller_count,
>                                           &rec_count_sum, &count_sum,
>                                           &called_without_ipa_profile))
> -    return false;
> +    {
> +      if (dump_file && (dump_flags & TDF_DETAILS))
> +     {
> +       fprintf (dump_file, " - skipping candidate value ");
> +       print_ipcp_constant_value (dump_file, val->value);
> +       fprintf (dump_file, " for ");
> +       ipa_dump_param (dump_file, ipa_node_params_sum->get (node), index);
> +       if (offset != -1)
> +         fprintf (dump_file, ", offset: " HOST_WIDE_INT_PRINT_DEC, offset);
> +       fprintf (dump_file, ": no relevant callers\n");
> +     }
> +      return false;
> +    }
>  
>    if (!dbg_cnt (ipa_cp_values))
>      return false;
> @@ -6167,22 +6179,33 @@ decide_whether_version_node (struct cgraph_node 
> *node, int cur_sweep)
>    if (info->node_dead || count == 0)
>      return false;
>  
> +  bool clone_for_all_contexts = node->local;
>    if (dump_file && (dump_flags & TDF_DETAILS))
> -    fprintf (dump_file, "\nEvaluating opportunities for %s.\n",
> -          node->dump_name ());
> +    {
> +      fprintf (dump_file, "\nEvaluating opportunities for %s.",
> +            node->dump_name ());
> +      if (clone_for_all_contexts)
> +     fprintf (dump_file, "  Will try to create a special all-context "
> +              "clone.\n");
> +      fprintf (dump_file, "\n");
> +    }
>  
>    auto_vec <cloning_opportunity_ranking, 32> opp_ranking;
>    for (int i = 0; i < count;i++)
>      {
>        if (!ipa_is_param_used (info, i))
> -     continue;
> +     {
> +       if (dump_file && (dump_flags & TDF_DETAILS))
> +         fprintf (dump_file, " - ignoring unused parameter %i.\n", i);
> +       continue;
> +     }
>  
>        class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
>        ipcp_lattice<tree> *lat = &plats->itself;
>        ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
>  
>        if (!lat->bottom
> -       && !lat->is_single_const ())
> +       && (!clone_for_all_contexts || !lat->is_single_const ()))
>       {
>         ipcp_value<tree> *val;
>         for (val = lat->values; val; val = val->next)
> @@ -6224,9 +6247,10 @@ decide_whether_version_node (struct cgraph_node *node, 
> int cur_sweep)
>         ipcp_value<tree> *val;
>         for (aglat = plats->aggs; aglat; aglat = aglat->next)
>           if (!aglat->bottom && aglat->values
> -             /* If the following is false, the one value has been considered
> +             /* If the following is false, the one value will be considered
>                  for cloning for all contexts.  */
> -             && (plats->aggs_contain_variable
> +             && (!clone_for_all_contexts
> +                 || plats->aggs_contain_variable
>                   || !aglat->is_single_const ()))
>             for (val = aglat->values; val; val = val->next)
>               {
> @@ -6240,7 +6264,7 @@ decide_whether_version_node (struct cgraph_node *node, 
> int cur_sweep)
>       }
>  
>        if (!ctxlat->bottom
> -       && !ctxlat->is_single_const ())
> +       && (!clone_for_all_contexts || !ctxlat->is_single_const ()))
>       {
>         ipcp_value<ipa_polymorphic_call_context> *val;
>         for (val = ctxlat->values; val; val = val->next)
> @@ -6283,19 +6307,21 @@ decide_whether_version_node (struct cgraph_node 
> *node, int cur_sweep)
>       }
>      }
>  
> +  if (!clone_for_all_contexts)
> +    return ret;
> +
>    struct caller_statistics stats;
>    init_caller_stats (&stats);
>    node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
>                                               false);
>    if (!stats.n_calls)
>      {
> -      if (dump_file)
> +      if (dump_file && (dump_flags & TDF_DETAILS))
>       fprintf (dump_file, "   Not cloning for all contexts because "
>                "there are no callers of the original node (any more).\n");
>        return ret;
>      }
>  
> -  bool do_clone_for_all_contexts = false;
>    ipa_auto_call_arg_values avals;
>    int removable_params_cost;
>    bool ctx_independent_const
> @@ -6304,60 +6330,10 @@ decide_whether_version_node (struct cgraph_node 
> *node, int cur_sweep)
>    if (ctx_independent_const || devirt_bonus > 0
>        || (removable_params_cost && clone_for_param_removal_p (node)))
>      {
> -       ipa_call_estimates estimates;
> -
> -      estimate_ipcp_clone_size_and_time (node, &avals, &estimates);
> -      sreal time = estimates.nonspecialized_time - estimates.time;
> -      time += devirt_bonus;
> -      time += hint_time_bonus (node, estimates);
> -      time += removable_params_cost;
> -      int size = estimates.size - stats.n_calls * removable_params_cost;
> -
> -      if (dump_file && (dump_flags & TDF_DETAILS))
> -     fprintf (dump_file, " - context independent values, size: %i, "
> -              "time_benefit: %f\n", size, (time).to_double ());
> -
> -      if (size <= 0 || node->local)
> -     {
> -       if (!dbg_cnt (ipa_cp_values))
> -         return ret;
> -
> -       do_clone_for_all_contexts = true;
> -       if (dump_file)
> -         fprintf (dump_file, "   Decided to specialize for all "
> -                  "known contexts, code not going to grow.\n");
> -     }
> -      else if (good_cloning_opportunity_p (node, time, stats.freq_sum,
> -                                        stats.count_sum, size,
> -                                        stats.called_without_ipa_profile,
> -                                        cur_sweep))
> -     {
> -       if (size + overall_size <= get_max_overall_size (node))
> -         {
> -           if (!dbg_cnt (ipa_cp_values))
> -             return ret;
> -
> -           do_clone_for_all_contexts = true;
> -           overall_size += size;
> -           if (dump_file)
> -             fprintf (dump_file, "   Decided to specialize for all "
> -                      "known contexts, growth (to %li) deemed "
> -                      "beneficial.\n", overall_size);
> -         }
> -       else if (dump_file && (dump_flags & TDF_DETAILS))
> -         fprintf (dump_file, "   Not cloning for all contexts because "
> -                  "maximum unit size would be reached with %li.\n",
> -                  size + overall_size);
> -     }
> -      else if (dump_file && (dump_flags & TDF_DETAILS))
> -     fprintf (dump_file, "   Not cloning for all contexts because "
> -              "!good_cloning_opportunity_p.\n");
> -    }
> +      if (!dbg_cnt (ipa_cp_values))
> +     return ret;
>  
> -  if (do_clone_for_all_contexts)
> -    {
>        auto_vec<cgraph_edge *> callers = node->collect_callers ();
> -
>        for (int i = callers.length () - 1; i >= 0; i--)
>       {
>         cgraph_edge *cs = callers[i];
> diff --git a/gcc/testsuite/gcc.dg/ipa/pr125207.c 
> b/gcc/testsuite/gcc.dg/ipa/pr125207.c
> new file mode 100644
> index 00000000000..9c48add4810
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/pr125207.c
> @@ -0,0 +1,43 @@
> +/* { dg-do run } */
> +/* { dg-options "-O3 --param ipa-cp-eval-threshold=1"  } */
> +
> +typedef int(*fp_t_5)(int, int, int);
> +int g2, g30, g21, g17, g16, g14, g9, g3, g12, g31;
> +_Bool g22, g26;
> +static
> +int f11(int, fp_t_5 a1, int, int, int)
> +{
> +    do {
> +      g12 = a1(1 ^ g14, 0, g9);
> +      if (g22)
> +        if (g2 != g17)
> +          if (g26)
> +            return g30;
> +    } while(true);
> +}
> +static __attribute__((noinline))
> +int f2(int a0, int, int a2)
> +{
> +    static int t = 0;
> +    t++;
> +    if (t > 2)
> +      __builtin_abort();
> +    g26 = true;
> +    g22 = true;
> +    if (g3 != a0) {
> +      g31 = 2;
> +      a0 = g31;
> +    }
> +    if (a0 == 2)
> +      if (a2 != 80)
> +        return 0;
> +    g17 = 6;
> +    g21 += f11(0, f2, 0, 0, 0);
> +    return 0;
> +}
> +void f1() { f2(0, 0, 0); }
> +int main()
> +{
> +    f2(0, 0, 0);
> +    return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-1.c b/gcc/testsuite/gcc.dg/vla-1.c
> index 52a1d9772d9..47f27b24961 100644
> --- a/gcc/testsuite/gcc.dg/vla-1.c
> +++ b/gcc/testsuite/gcc.dg/vla-1.c
> @@ -1,7 +1,7 @@
>  /* { dg-do compile } */
>  /* { dg-options "-g -O3 -fdump-tree-optimized -fvar-tracking-assignments 
> -fno-selective-scheduling -fno-selective-scheduling2 -fno-ipa-vrp --param 
> ipa-cp-eval-threshold=1" } */
>  
> -int __attribute__((noinline))
> +static int __attribute__((noinline))
>  f1 (int i)
>  {
>    char a[i + 1];
> diff --git a/libgomp/testsuite/libgomp.c/ipcp-cb-spec1.c 
> b/libgomp/testsuite/libgomp.c/ipcp-cb-spec1.c
> index a2ab03f692a..fa0d27b0f92 100644
> --- a/libgomp/testsuite/libgomp.c/ipcp-cb-spec1.c
> +++ b/libgomp/testsuite/libgomp.c/ipcp-cb-spec1.c
> @@ -4,15 +4,21 @@
>  /* { dg-options "-O3 -fopenmp -std=gnu99 -fdump-ipa-cp-details --param 
> ipa-cp-eval-threshold=1" } */
>  /* { dg-require-effective-target fopenmp } */
>  
> -void test(int c) {
> +static inline void __attribute__((always_inline)) test(int c) {
>    for (int i = 0; i < c; i++)
>      if (!__builtin_constant_p(c))
>        __builtin_abort();
>  }
> -int main() {
> +
> +int foo(int c) {
>  #pragma omp task
> -  test(7);
> +  test(c);
> +  return 0;
> +}
> +
> +int main() {
> +  foo(7);
>    return 0;
>  }
>  
> -/* { dg-final { scan-ipa-dump "Creating a specialized node of main._omp_fn" 
> "cp" } } */
> +/* { dg-final { scan-ipa-dump "Creating a specialized node of foo._omp_fn" 
> "cp" } } */
> -- 
> 2.54.0

Reply via email to