Skip to content

Change for loops from min/extent to min/max - #8858

Merged
abadams merged 10 commits into
mainfrom
abadams/min_max_for
Dec 9, 2025
Merged

Change for loops from min/extent to min/max#8858
abadams merged 10 commits into
mainfrom
abadams/min_max_for

Conversation

@abadams

@abadams abadams commented Nov 4, 2025

Copy link
Copy Markdown
Member

This is an experiment in changing how we represent for loops from min/extent to min/max. In C terms, it's a change from this:

for (int x = min; x < min + extent; x++) {}

to this:

for (int x = min; x <= max; x++) {}

The reason to do this is that we need the max anyway for bounds inference, so we keep it around awkwardly as a .loop_max symbol, and then need to jump through hoops to preserve it. My thought was maybe we should just represent loops using the max to begin with, because we don't need the extent nearly so much, and we can just compute it on the fly when needed.

The result is a net deletion of code, and up to 2x faster lowering (because we can delete unused lets earlier in lowering).

I'm not comfortable merging this yet, as it's a big change to a core data type, but I wanted to open it as a PR to get buildbot testing to see what's broken.

Edit: I forgot to say the original motivation - sliding window. With a min/max loop you just bump the min in isolation. With a min/extent loop you have to also subtract from the extent, and then hope things simplify later after loop partitioning.

Second Edit: After dev meeting discussion, I think this is a good thing to merge

Now they're just regular old lets placed there for the convenience of
computing splits. They don't need to accurately reflect the loop, or be
preserved through lowering passes, etc. loop_extent was deleted
entirely. They do need to be preserved until computation bounds
inference because they provide a way to compute loop bounds as a
function of .min .max symbols that don't exist until after that point,
so you can't move them around before then.

This change was possible because allocation bounds inference doesn't
need any assistance given loops that are min/max instead of min/extent.
Also, simplify the loop extents when offloading GPU kernels and add
extra outputs in the bgu makefile.
@abadams
abadams requested a review from alexreinking December 2, 2025 20:23

@alexreinking alexreinking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits/questions

Comment thread src/BoundConstantExtentLoops.cpp
Comment thread src/IR.h
Comment thread src/Lower.cpp

debug(1) << "Simplifying...\n";
s = simplify(s, false); // Storage folding and allocation bounds inference needs .loop_max symbols
s = simplify(s);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a great opportunity to simplify the simplifier! (In a follow up PR of course)

Comment thread src/PartitionLoops.cpp Outdated
Comment thread src/PartitionLoops.cpp Outdated
Comment thread src/RebaseLoopsToZero.cpp
Comment thread src/VectorizeLoops.cpp Outdated
abadams and others added 2 commits December 8, 2025 10:05
Co-authored-by: Alex Reinking <areinking@adobe.com>
@alexreinking
alexreinking self-requested a review December 8, 2025 18:55
@abadams
abadams merged commit f25bfc3 into main Dec 9, 2025
15 of 19 checks passed
@alexreinking
alexreinking deleted the abadams/min_max_for branch December 9, 2025 21:39
alexreinking added a commit that referenced this pull request Aug 14, 2026
* Constant-fold a*0 ~> 0 in SolveExpression

* Solve c<=min(a,b) and friends at the interval level

solve_for_{inner,outer}_interval (used by loop partitioning) had rewrites
for a Min/Max on the side being solved for (min(a,b) <= c), but none for
a Min/Max on the *other* side (c <= min(a,b)).

This became necessary when #8858 changed the representation of extent.
We now get expressions of the form

    let bound = min(next_vector_boundary, extent) in (idx <= bound)

in GuardWithIf loops where the extent isn't a compile-time multiple of
the vector width. Without these rules, loop partitioning can't remove
these guards in the steady-state.

This PR adds the rule `c <= min(a,b) <=> c<=a && c<=b` (and the analogous
rules for max/>=) to the solver. We also add two supporting rules: folding
`a * 0` to `a`, and handling inequalities that do not mention the solved
variable via `can_prove` (e.g. `x <= 16 && 0 <= 16`).

* Add regression tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants