Skip to content

ConstraintAnalysis: Optimize loops - #8980

Merged
kripken merged 135 commits into
WebAssembly:mainfrom
kripken:loops
Aug 11, 2026
Merged

ConstraintAnalysis: Optimize loops#8980
kripken merged 135 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 thread src/passes/ConstraintAnalysis.cpp Outdated
// 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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comment added, and code un-nested.

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

@kripken

kripken commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Or are you saying that the pass will hang on loops like this, and that's ok because they're rare?

Oh, no, that would not be ok 😄 We do not hang in any case here, the assertion from the last PR verifies that.

I would expect the pass to be powerful enough for this analysis.

We can consider adding it later, certainly, but that loop is rare and not trivial to optimize (e.g. if y, in that loop, rose faster than x, the behavior would be very different).

@tlively

tlively commented Aug 11, 2026

Copy link
Copy Markdown
Member

I checked locally with that loop and I see that we cannot determine that x is still less than y after incrementing x twice, probably due to the limitation we discussed here: #8969 (comment). So I still think we would need a more general widening fallback if we made the analysis more powerful in reasonable ways, but I guess we maybe don't need it now.

@kripken
kripken merged commit 6906bf0 into WebAssembly:main Aug 11, 2026
16 checks passed
@kripken
kripken deleted the loops branch August 11, 2026 19:03
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