> On 25 Aug 2026, at 18:24, Jan Hubicka <[email protected]> wrote:
> 
>> 
>> 
>>> On 25 Aug 2026, at 12:36, Richard Biener <[email protected]> wrote:
>>> 
>>> On Mon, Aug 24, 2026 at 8:40 PM <[email protected]> wrote:
>>>> 
>>>> From: Kyrylo Tkachov <[email protected]>
>>>> 
>>>> branch_prob () cannot place a counter on an abnormal edge.  It
>>>> represents the flow of a computed goto with fake edges.  One fake edge
>>>> goes from the entry block to each target.  Another goes from the
>>>> dispatch block to the exit block.  branch_prob () also marks the
>>>> abnormal edges as ignored.  An ignored edge that is not on the spanning
>>>> tree is not instrumented.  The graph solver does not infer its count.
>>>> Its count therefore remains at the default zero, whatever the program
>>>> did.
>>>> 
>>>> compute_branch_probabilities () then turns that zero into a precise
>>>> never.  A precise never is proof of deadness.
>>>> propagate_unlikely_bbs_forward () cannot reach any dispatch target from
>>>> the entry block, and determine_unlikely_bbs () zeroes the measured
>>>> counts of the whole region.  For a threaded interpreter that region is
>>>> the entire hot loop.  Thus, -fprofile-use reads measured block counts
>>>> and then discards them.
>>>> 
>>>> For
>>>> 
>>>> static const void *tab[] = { &&L_ADD, ... };
>>>> goto *tab[*pc++];
>>>> 
>>>> -fdump-ipa-profile shows 5000 executions for the dispatch block and 1000
>>>> for each handler.  The optimized dump then reads
>>>> 
>>>> ;; basic block 5, loop depth 1, count 0 (precise), probably never executed
>>>> 
>>>> for every one of them.
>>>> 
>>>> Record the probability of an ignored edge outside the spanning tree as a
>>>> guess.  A guessed never is still a zero probability.  It does not invent
>>>> a split between the dispatch targets.  A target with a measured zero
>>>> count also stays at zero.  The guessed probability stops the unmeasured
>>>> edge count from being evidence that the target is dead.  The same dump
>>>> now reads
>>>> 
>>>> ;; basic block 5, loop depth 1, count 5000 (precise), maybe hot
>>>> 
>>>> The rule is applied per edge and attributes no flow.  It does not depend
>>>> on how many dispatch blocks a function has.  It also needs no notion of
>>>> which dispatch a target belongs to.
>>>> 
>>>> Evaluated on aarch64 (NVIDIA Vera) with CPython 3.14.5 at -O3.  CPython
>>>> was configured with --enable-optimizations and --with-lto.  In
>>>> Python/ceval.c, the share of the basic blocks of
>>>> _PyEval_EvalFrameDefault left with a precise zero count falls from 98.3%
>>>> to 23.1%.  During the full-program LTO link, the function grows by 0.7%
>>>> and the interpreter text grows by 0.1%.  A fixed-work
>>>> pyperformance-derived workload takes 2.5% fewer cycles.
>>>> 
>>>> Bootstrapped and tested on aarch64-none-linux-gnu.
>>> 
>>> Wouldn't UNINITIALIZED be what actually happens?  We simply don't know
>>> due to lack of counters.  Or do we ICE on that later, requiring sth else 
>>> than
>>> UNINITIALIZED?
>> 
>> Yes, UNINITIALIZED describes the information we have, but it cannot remain 
>> in that state here.
>> The earlier profile-estimation pass has already set cfun->cfg->full_profile.
>> I tested setting these edge probabilities to UNINITIALIZED. The cleanup 
>> after branch_prob () then reports “probability of edge ... not initialized” 
>> and fails with verify_flow_info failed.
> 
> The problem with unitialized values is that you can not really drop them
> meaningfully into the cost model (i.e. estimate cost of a  spill in
> register allocator or benefit of inline).  At the end of day we need
> actual numbers to compute prioriites on.  So once profile is build
> everyone is supposed to fill the unknowns by reasonable guesses.
> 
> Guessed 0 is kind of dangerous value for computed goto if it lives on a hot
> path (which computed gotos may).  It means that the 0 may get propagated
> further and code on hot path become cold.  Also BB reorder will then not
> place the computed goto close to its likely target which is probably
> still useful optimization

Ok, another approach could be to initialize these edges with a share of the 
remaining “unaccounted for” profile.  That seems to also work for the Cpython 
case:
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728947.html
Thanks,
Kyrill

> 
> Honza
>> 
>> Thanks,
>> Kyrill


Reply via email to