Ping.
Thanks,
Kyrill
> On 8 Sep 2026, at 18:03, Kyrylo Tkachov <[email protected]> wrote:
>
> Ping.
> https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728947.html
> Thanks,
> Kyrill
>
>> On 26 Aug 2026, at 08:29, Kyrylo Tkachov <[email protected]> wrote:
>>
>> From: Kyrylo Tkachov <[email protected]>
>>
>> This is an alternative to
>> https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728756.html
>> following Honza's comments at:
>> https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728898.html
>>
>> branch_prob () cannot place a counter on an abnormal edge. A computed
>> goto uses fake edges to represent its possible targets. branch_prob ()
>> also marks the abnormal edges as ignored. An ignored edge outside the
>> spanning tree gets neither a counter nor an inferred count.
>>
>> compute_branch_probabilities () converts the default zero count of such
>> an edge to a precise never. determine_unlikely_bbs () can then clear
>> measured counts from a region that is reachable only through these
>> edges. This can make the hot loop of a threaded interpreter appear
>> dead. A zero probability also gives later cost models no useful
>> dispatch target.
>>
>> For a five-target dispatch, the edge dump changes from
>>
>> [never] count:0 (precise)
>>
>> to
>>
>> [20.0% (guessed)] count:1000 (guessed)
>>
>> and the measured dispatch count of 5000 survives.
>>
>> Keep the initial zero probability as a guess while partial-profile
>> handling runs. After fake edges are removed, subtract the probabilities
>> of other successors from always and divide the remainder evenly among
>> ignored off-tree successors of blocks with a nonzero IPA count. Mark
>> the result as guessed. This gives computed-goto targets a normalized,
>> usable distribution. If other successors consume all probability, the
>> ignored edges remain guessed zero and the known distribution stays
>> unchanged.
>>
>> Add tests for an all-unknown computed-goto dispatch and a returns-twice
>> source with both measured and ignored successors.
>>
>> Evaluated on aarch64 (NVIDIA Vera) with CPython 3.14.5 at -O3. CPython
>> was configured with --enable-optimizations and --with-lto. In
>> _PyEval_EvalFrameDefault, the share of basic blocks with a precise zero
>> count falls from 98.5% to 22.6%. A fixed-work pyperformance-derived
>> workload takes 1.7% fewer cycles.
>>
>> Bootstrapped and regression tested on aarch64-none-linux-gnu.
>> Ok for trunk?
>> thanks,
>> Kyrill
>>
>> gcc/ChangeLog:
>>
>> * profile.cc (guess_unmeasured_edge_probabilities): New function.
>> (compute_branch_probabilities): Mark ignored off-tree edge
>> probabilities as guessed.
>> (branch_prob): Divide the remaining probability evenly among ignored
>> off-tree successors.
>>
>> gcc/testsuite/ChangeLog:
>>
>> * gcc.dg/tree-prof/comp-goto-2.c: New test.
>> * gcc.dg/tree-prof/setjmp-2.c: New test.
>>
>> Signed-off-by: Kyrylo Tkachov <[email protected]>
>> ---
>> gcc/profile.cc | 44 ++++++++++++++
>> gcc/testsuite/gcc.dg/tree-prof/comp-goto-2.c | 62 ++++++++++++++++++++
>> gcc/testsuite/gcc.dg/tree-prof/setjmp-2.c | 29 +++++++++
>> 3 files changed, 135 insertions(+)
>> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/comp-goto-2.c
>> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/setjmp-2.c
>>
>> diff --git a/gcc/profile.cc b/gcc/profile.cc
>> index 5ae64d4043f..34e797b918b 100644
>> --- a/gcc/profile.cc
>> +++ b/gcc/profile.cc
>> @@ -447,6 +447,38 @@ cmp_stats (const void *ptr1, const void *ptr2)
>> return 0;
>> }
>>
>> +/* Set probabilities of ignored off-tree successors of BB to an even share
>> of
>> + the probability not assigned to other successors. Fake edges must have
>> + been removed. */
>> +
>> +static void
>> +guess_unmeasured_edge_probabilities (basic_block bb)
>> +{
>> + profile_probability remainder = profile_probability::always ();
>> + edge e;
>> + edge_iterator ei;
>> + unsigned unmeasured = 0;
>> +
>> + FOR_EACH_EDGE (e, ei, bb->succs)
>> + {
>> + gcc_checking_assert (!(e->flags & EDGE_FAKE));
>> + if (EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
>> + unmeasured++;
>> + else
>> + {
>> + gcc_checking_assert (e->probability.initialized_p ());
>> + remainder -= e->probability;
>> + }
>> + }
>> +
>> + if (!unmeasured)
>> + return;
>> +
>> + profile_probability probability = (remainder / unmeasured).guessed ();
>> + FOR_EACH_EDGE (e, ei, bb->succs)
>> + if (EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
>> + e->probability = probability;
>> +}
>>
>> /* Compute the branch probabilities for the various branches.
>> Annotate them accordingly.
>> @@ -719,6 +751,12 @@ compute_branch_probabilities (unsigned cfg_checksum,
>> unsigned lineno_checksum)
>> bool prev_never = e->probability == profile_probability::never ();
>> e->probability = profile_probability::probability_in_gcov_type
>> (edge_gcov_count (e), bb_gcov_count (bb));
>> + /* Mark an unmeasured zero as guessed so it does not prove
>> + deadness or change other probabilities during partial-profile
>> + handling. A guess based on the remaining probability is
>> + assigned after fake edges are removed. */
>> + if (EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
>> + e->probability = e->probability.guessed ();
>> if (e->probability == profile_probability::never ()
>> && !prev_never
>> && flag_profile_partial_training)
>> @@ -1762,6 +1800,12 @@ branch_prob (bool thunk)
>>
>> remove_fake_edges ();
>>
>> + if (flag_branch_probabilities
>> + && profile_status_for_fn (cfun) == PROFILE_READ)
>> + FOR_EACH_BB_FN (bb, cfun)
>> + if (bb->count.ipa ().nonzero_p ())
>> + guess_unmeasured_edge_probabilities (bb);
>> +
>> if (condition_coverage_flag || path_coverage_flag || profile_arc_flag)
>> gimple_init_gcov_profiler ();
>>
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/comp-goto-2.c
>> b/gcc/testsuite/gcc.dg/tree-prof/comp-goto-2.c
>> new file mode 100644
>> index 00000000000..f3079c938a7
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/comp-goto-2.c
>> @@ -0,0 +1,62 @@
>> +/* The ignored edges out of a computed goto are outside the spanning tree
>> and
>> + have no individual counts. Check that -fprofile-use keeps the measured
>> + block counts and assigns a usable guessed distribution to these edges.
>> */
>> +/* { dg-options "-O2 -fdump-tree-optimized-blocks-details" } */
>> +/* { dg-require-effective-target indirect_jumps } */
>> +/* { dg-require-effective-target label_values } */
>> +
>> +enum { OP_ADD, OP_SUB, OP_MUL, OP_STORE, OP_HALT, NOPS };
>> +
>> +unsigned char code[8];
>> +long mem[4] = { 1, 2, 3, 4 };
>> +
>> +__attribute__((noinline)) long
>> +interp (const unsigned char *pc)
>> +{
>> + static const void *const tab[NOPS]
>> + = { &&L_ADD, &&L_SUB, &&L_MUL, &&L_STORE, &&L_HALT };
>> + long acc = 0;
>> +
>> +#define DISPATCH() goto *tab[*pc++]
>> +
>> + DISPATCH ();
>> +L_ADD:
>> + acc += mem[*pc++];
>> + DISPATCH ();
>> +L_SUB:
>> + acc -= mem[*pc++];
>> + DISPATCH ();
>> +L_MUL:
>> + acc *= 3;
>> + DISPATCH ();
>> +L_STORE:
>> + mem[*pc++] = acc;
>> + DISPATCH ();
>> +L_HALT:
>> + return acc;
>> +}
>> +
>> +int
>> +main (void)
>> +{
>> + long r = 0;
>> + int i;
>> +
>> + code[0] = OP_ADD; code[1] = 1;
>> + code[2] = OP_SUB; code[3] = 2;
>> + code[4] = OP_MUL;
>> + code[5] = OP_STORE; code[6] = 3;
>> + code[7] = OP_HALT;
>> +
>> + for (i = 0; i < 1000; i++)
>> + r += interp (code);
>> +
>> + return r == 0;
>> +}
>> +
>> +/* No block of the dispatch loop may be left with a precise zero count. */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump-not "count 0 \\(precise"
>> "optimized" } } */
>> +/* Each of the five ignored edges receives 20%. The detailed dump prints
>> each
>> + edge in both a successor and predecessor list. */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump-times "20.0% \\(guessed\\)]
>> count:1000 \\(guessed" 10 "optimized" } } */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump-not "Invalid sum of outgoing
>> probabilities" "optimized" } } */
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/setjmp-2.c
>> b/gcc/testsuite/gcc.dg/tree-prof/setjmp-2.c
>> new file mode 100644
>> index 00000000000..0adee16506b
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/setjmp-2.c
>> @@ -0,0 +1,29 @@
>> +/* A returns-twice call has a measured fallthrough edge and an ignored
>> + abnormal edge. Check that guessing the abnormal edge does not change the
>> + measured probability when no probability remains to distribute. */
>> +/* { dg-options "-O2 -fdump-tree-feedback_fnsplit2-blocks-details" } */
>> +/* { dg-require-effective-target nonlocal_goto } */
>> +
>> +static void *buf[20];
>> +
>> +__attribute__((noinline)) static int
>> +f (void)
>> +{
>> + return __builtin_setjmp (buf) + 1;
>> +}
>> +
>> +int
>> +main (void)
>> +{
>> + int sum = 0;
>> +
>> + for (int i = 0; i < 1000; ++i)
>> + sum += f ();
>> +
>> + return sum != 1000;
>> +}
>> +
>> +/* { dg-final-use-not-autofdo { scan-tree-dump
>> "always]\[^\\n\\r\]*count:1000
>> \\(precise,\[^\\n\\r\]*\\(FALLTHRU,EXECUTABLE\\)" "feedback_fnsplit2" } } */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump "never
>> \\(guessed\\)]\[^\\n\\r\]*\\(ABNORMAL,EXECUTABLE\\)" "feedback_fnsplit2" } }
>> */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump-not "Invalid sum of outgoing
>> probabilities" "feedback_fnsplit2" } } */
>> +/* { dg-final-use-not-autofdo { scan-tree-dump-not "Invalid sum of incoming
>> counts" "feedback_fnsplit2" } } */
>> --
>> 2.50.1 (Apple Git-155)
>>
>