BPF List
 help / color / mirror / Atom feed
From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: "Eduard Zingerman" <eddyz87@gmail.com>, <bpf@vger.kernel.org>
Cc: <daniel@iogearbox.net>, <andrii@kernel.org>, <memxor@gmail.com>
Subject: Re: [PATCH bpf-next 16/37] bpf: Count loops whose first value or bound is a range
Date: Wed, 04 Nov 2026 18:25:36 +0000	[thread overview]
Message-ID: <DMMMYOKCCSS2.15GTRVXFVMHTL@gmail.com> (raw)
In-Reply-To: <f1e784a5de228293bc4fe21a5cfcc60d93a4400d.camel@gmail.com>

On Tue, Nov 03, 2026 at 11:26 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
> On Tue, 2026-11-03 at 23:40 +0000, Alexei Starovoitov wrote:
>
>> +/* What is known about a scalar or about the offset of a pointer. */
>>  struct scev_value {
>> -	u64 value;	/* scalar value or a pointer offset */
>> +	u64 umin, umax;
>> +	s64 smin, smax;
>> +	u32 u32_min, u32_max;
>> +	s32 s32_min, s32_max;
>>  	int ptr_reg;	/* SCEV register identifying the pointer origin, or -1. */
>>  };
>
> Nice set of improvements.

Thanks for the quick review. Much appreciate it.

> Quick question: can the above use cnum{32,64} instead of direct bounds?
> Reason being, that I thought compute_backedge_taken_count() can be
> expressed in terms of cnums as well, in which case there would be no
> need to split it as two functions.

Yes. Will switch to r64 + r32 in a follow up.
scev_value_add() becomes cnum64_add() + cnum32_add().
It's more precise too. [-1, 0] + 1 is [0, 1] for an unsigned compare.
The direct bounds make it [0, U64_MAX].

cnums don't remove the split though. They merge signed and unsigned.
The split is between constants and ranges.

> cnums aside, it appears surprising that both compute_backedge_taken_count()
> and compute_backedge_taken_max() are necessary.
> For example, I'd expect that compute_backedge_taken_max() should be
> expressible using compute_backedge_taken_count() as:
>
>   high  := compute_backedge_taken_count(..., min(initial), step, max(bound), ...);
>   low   := compute_backedge_taken_count(..., max(initial), step, min(bound), ...);
>   ... overflow check for iv with `low` and `high` ...
>
> Or vice-versa, compute_backedge_taken_count() seem to be expressible
> as a special case for compute_backedge_taken_max() with ranges
> [initial, initial] and [bound, bound].
>
> Is there some precision loss I don't understand?

well... Below _count() and _max() are the two functions.

The problem

The value that ends the loop depends on (bound - first value) % step.

  for (u64 i = first; i > 0; i -= 2)

  first   i at the latch     _count()
  -----   ----------------   ---------------------
  10      10 8 6 4 2 0       5
  11      11 9 7 5 3 1 -1    none, -1 wraps around
  12      12 10 8 6 4 2 0    6

For 10..12 both ends have a count, and 11 wraps around.
A range needs a check that holds for each remainder.
The value that ends the loop is less than a step behind the bound.
Here the worst one is 0 - 2 + 1 = -1, so 10..12 has no count.
The same check rejects [10, 10], and _count() accepts 10.

The options

Four ways to get a count for constants and for ranges:

  option                           what breaks
  -------------------------------  ------------------------------
  _count() for constants and       nothing, it is two functions
  _max() for ranges (the patch)
  _max() from two _count() calls   11 in the example
  _count() as _max() of [c, c]     constants lose counts
  one function on top of cnums     the same as [c, c]

Two _count() calls

The overflow check in _count() is exact for one first value.
It passes for both ends in the example, so it says nothing about 11.
The check has to come from the bound, and that is what _max() does.
_count() also returns false when the latch is false at first.
'low' of 'i = 0; i < (n & 15)' is _count(0, 1, 0), which is false.
The division is all that the two can share.

_max() of [c, c]

The two differ in what must not wrap around and in '!='.

                _count()                     _max()
  -----------   --------------------------   ----------------------------
  wrap around   first + step * count         bound + step - 1 for '<'
  '!='          step divides bound - first   step is 1 or -1, each first
                                             value in front of each bound

post32_lt, post32_sgt and post32_slt_max end the loop at U32_MAX,
S32_MIN and S32_MAX. The check of _max() gives them no count.
pre_ne, post_ne and pre_ne_wrap_up have a step of 2.
The '!=' rule of _max() gives them no count.

_max() can restore both with branches for constants.
That is the same two calculations in one function.
imo it's better to keep _count() as is.
It's exact and the tests cover it.

But I'm open to any other approach that my bots couldn't think of.

> Also, you'd consider it a nit, but compute_backedge_taken_count() has
> a nice set of comments explaining where each condition is coming from.

Makes sense. Will add such comments to _max() in follow up.


  reply	other threads:[~2026-11-04 18:25 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 23:40 [PATCH bpf-next 00/37] bpf: More precision, more counted loops, larger limits Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 01/37] bpf: Keep the range of a packet pointer when a number >= 0 is added Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 02/37] selftests/bpf: Test range of a packet pointer after a number " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 03/37] bpf: Refuse fexit on an unreliable static subprog that returns in R0:R2 Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 04/37] bpf: Don't trust BTF of a static subprog that reads more registers Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 05/37] selftests/bpf: Test trampolines on subprogs that take more than BTF says Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 06/37] bpf: Count stack arguments of a static subprog from its code Alexei Starovoitov
2026-11-04  0:14   ` sashiko-bot
2026-11-04  1:05     ` Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 07/37] selftests/bpf: Test static subprogs that don't take what BTF says Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 08/37] bpf: Don't treat a partial spill as widened in scalar evolution Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 09/37] selftests/bpf: Test loops that change a partial spill Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 10/37] bpf: Let scalar evolution see loads from read-only maps Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 11/37] selftests/bpf: Test scalar evolution with a loop bound in .rodata Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 12/37] bpf: Helpers don't write through ARG_ANYTHING Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 13/37] selftests/bpf: Test scalar evolution with stack address as ARG_ANYTHING Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 14/37] bpf: Fold sums of constants and two shifts by 32 in scalar evolution Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 15/37] selftests/bpf: Test sums of constants and " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 16/37] bpf: Count loops whose first value or bound is a range Alexei Starovoitov
2026-11-04  0:21   ` sashiko-bot
2026-11-04  1:05     ` Alexei Starovoitov
2026-11-04  7:26   ` Eduard Zingerman
2026-11-04 18:25     ` Alexei Starovoitov [this message]
2026-11-03 23:40 ` [PATCH bpf-next 17/37] selftests/bpf: Test " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 18/37] bpf: Describe more registers of a counted loop Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 19/37] selftests/bpf: Test " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 20/37] bpf: Compute scev for a loop that has more than 256 exits Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 21/37] selftests/bpf: Test a loop with 301 exits Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 22/37] bpf: Describe a 32-bit counter that lives on the stack Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 23/37] selftests/bpf: Test " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 24/37] bpf: Count a loop with several back edges Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 25/37] selftests/bpf: Test loops " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 26/37] bpf: Compute bounds for a shift, a division and a remainder by a range Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 27/37] selftests/bpf: Test " Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 28/37] bpf: Count a loop whose latch is false in the first iteration Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 29/37] bpf: Count '!=' across 0 in 32 bits and loops that go down Alexei Starovoitov
2026-11-04  0:26   ` sashiko-bot
2026-11-04  1:06     ` Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 30/37] bpf: Count a loop whose latch tests a flag that a jump sets Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 31/37] selftests/bpf: Test more latches of counted loops Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 32/37] bpf: Allow BPF_MAX_SUBPROGS targets of a read from a table of functions Alexei Starovoitov
2026-11-04  0:12   ` sashiko-bot
2026-11-04  1:06     ` Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 33/37] bpf: Keep the frame of analyze_subprog() small Alexei Starovoitov
2026-11-03 23:40 ` [PATCH bpf-next 34/37] bpf: Allow 24 arguments of static subprogs Alexei Starovoitov
2026-11-03 23:41 ` [PATCH bpf-next 35/37] selftests/bpf: Test a static subprog with 24 arguments Alexei Starovoitov
2026-11-03 23:41 ` [PATCH bpf-next 36/37] bpf: Bump BPF_MAX_SUBPROGS to 1024 Alexei Starovoitov
2026-11-03 23:41 ` [PATCH bpf-next 37/37] selftests/bpf: Make room in veristat for the stats of 1024 subprogs Alexei Starovoitov
2026-11-04 18:40 ` [PATCH bpf-next 00/37] bpf: More precision, more counted loops, larger limits patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DMMMYOKCCSS2.15GTRVXFVMHTL@gmail.com \
    --to=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=memxor@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox