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) >
