Skip to content

ConstraintAnalysis: Optimize loops - #8980

Open
kripken wants to merge 132 commits into
WebAssembly:mainfrom
kripken:loops
Open

ConstraintAnalysis: Optimize loops#8980
kripken wants to merge 132 commits into
WebAssembly:mainfrom
kripken:loops

Conversation

@kripken

@kripken kripken commented Aug 7, 2026

Copy link
Copy Markdown
Member

One Weird Trick is enough: eagerly extend ranges of constants:

x = C
branch on x < D where C < D
=>
x >= C && x < D

This is simpler than the typical approach used in Abstract Interpretation,
as we do it eagerly (immediately on a branch). This eagerness might
lose some precision, but not in cases we care about, I don't think:
if x is a constant and we branch on it, then it must be a loop variable
that will increment (if it isn't in a loop, the constant x would have been
propagated to the branch by other passes).

@kripken
kripken requested a review from tlively August 7, 2026 18:47
@kripken
kripken requested a review from a team as a code owner August 7, 2026 18:47

@tlively tlively 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.

Nice!

I'm concerned we'll still need a more conservative fallback, though. Consider this loop:

x = 0
y = 100
while (x < y) {
  ++x;
  ++x;
  ++y;
}

This loop will also execute 100 times, and hopefully we are able to analyze all the individual operations, but the loop condition does not contain a constant, so this particular widening heuristic will not apply.

// comment).
if (auto* M = std::get_if<Literal>(&branch.constraint.term)) {
auto localConstraints = constraints.get(branch.local);
if (localConstraints.size() == 1 &&

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.

Probably worth a comment about why we only handle the case where there is only a single constraint.

It also might be easier to read if we invert the conditions and early return false when our expectations are not met.

@kripken

kripken commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

This loop will also execute 100 times, and hopefully we are able to analyze all the individual operations, but the loop condition does not contain a constant, so this particular widening heuristic will not apply.

Loops where the bound changes (not just y = array.len(ref)) are definitely harder to handle, but also very rare, I think... I wasn't planning to support them. I think that would require a general Scalar Evolution analysis like LLVM has.

@tlively

tlively commented Aug 10, 2026

Copy link
Copy Markdown
Member

Are you saying the pass will already stop early with that sample program because it is not sophisticated enough to prove that the loop condition still holds after the first iteration? Or are you saying that the pass will hang on loops like this, and that's ok because they're rare?

If the former, that is surprising because I would expect the pass to be powerful enough for this analysis.

If the latter, then I think it would be worth adding a fallback widening mechanism to avoid performance cliffs. You already track the visitation count in debug code, so widening to top when the visitation count gets too high should be simple enough.

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