Re: [PATCH] [AutoFDO][IPA-CP] Handle zero-caller locals after AutoFDO early inlining

Jan Hubička <[email protected]> Tue, 4 Aug 2026 08:49:52 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <CAJf+ejeQyteMrW0Gezrbw-F=sVOdLXxnMHC=FSOmhx4AEHvRPg@mail.gmail.com>
Hi,

On Tue, Aug 4, 2026 at 2:38 AM Kugan Vivekanandarajah <
[email protected]> wrote:

> AutoFDO early inlining can inline all direct calls to a local
> function while speculative call promotion keeps the function
> address-taken.  The function consequently remains in the call graph
> with no direct callers, triggering the -ftoplevel-reorder checking
> assertion in IPA-CP.
>
> Ordinary IPA devirtualization does not normally produce this state at
> IPA-CP: it creates speculative edges before IPA-CP, but those edges are
> inlined only by the later IPA inliner.  IPA-CP therefore still sees a
> direct caller.  AutoFDO instead performs speculative inlining early, so
> only its IPA_REF_ADDR may remain by the time IPA-CP runs.
>
> When AutoFDO inlining is enabled, recognize zero-caller functions that
> cannot be removed after their direct calls disappear and initialize
> their lattices as variable.  Preserve the assertion for removable
> locals and non-AutoFDO compilation.
>
> gcc/ChangeLog:
>
>         * ipa-cp.cc (initialize_node_lattices): Handle zero-caller local
>         functions left after AutoFDO early inlining.
>

I think the problem here is earlier.  If function is local, it can not be
called
indirectly and thus it makes no sense to add speculative call to it.
I am not sure how the auto-FDO profile data got to a conclussion that
the function can be called indirectly - I can see it can be triggered when
source code changes between train run and compilation. Can you perhaps
check what is happening and if something like this works?

diff --git a/gcc/auto-profile.cc b/gcc/auto-profile.cc
index 6aa5166c748..63ca8938146 100644
--- a/gcc/auto-profile.cc
+++ b/gcc/auto-profile.cc
@@ -3326,6 +3333,14 @@ afdo_indirect_call (gcall *stmt, const
icall_target_map &map,
                 afdo_string_table->get_symbol_name (max_iter->first));
       return false;
     }
+  if (direct_call->local)
+    {
+      if (dump_file)
+       fprintf (dump_file, "AFDO Indirect call target %s "
+                "is local and thus can not be called indirectly\n",
+                afdo_string_table->get_symbol_name (max_iter->first));
+      return false;
+    }

   callee = gimple_call_fn (stmt);



>
> Regression tested on aarch64-linux-gnu with no new regressions
>
> Signed-off-by: Kugan Vivekanandarajah <[email protected]>
> ---
>  gcc/ipa-cp.cc | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
> index c1b1f6201fd3..d68952ec8711 100644
> --- a/gcc/ipa-cp.cc
> +++ b/gcc/ipa-cp.cc
> @@ -1443,7 +1443,18 @@ initialize_node_lattices (struct cgraph_node *node)
>                                                   NULL, true);
>        else if (caller_count == 0)
>         {
> -         gcc_checking_assert (!opt_for_fn (node->decl,
> flag_toplevel_reorder));
> +         /* With -ftoplevel-reorder, a local body with no direct callers
> +            should already have been removed.  AutoFDO early-inline of a
> +            speculative call expands via gimple_ic (&target), so after all
> +            direct call edges are inlined the local can remain
> address-taken
> +            with unknown indirect uses; treat that as VARIABLE.  Ordinary
> +            IPA devirtualization does not need this exception: it creates
> +            speculative edges before IPA-CP, but inlines them only later,
> so
> +            IPA-CP still sees a direct caller.  */
> +         if (!(flag_auto_profile && flag_auto_profile_inlining
> +               && !node->can_remove_if_no_direct_calls_p (false)))
> +           gcc_checking_assert
> +             (!opt_for_fn (node->decl, flag_toplevel_reorder));
>           variable = true;
>         }
>      }
> --
> 2.34.1
>
>