Skip to content

ConstraintAnalysis: Use spans to prove things - #9025

Open
kripken wants to merge 34 commits into
WebAssembly:mainfrom
kripken:i65_span.use
Open

ConstraintAnalysis: Use spans to prove things#9025
kripken wants to merge 34 commits into
WebAssembly:mainfrom
kripken:i65_span.use

Conversation

@kripken

@kripken kripken commented Aug 20, 2026

Copy link
Copy Markdown
Member

When two constraints can be expressed as spans, see if one shows the
other is true or not.

Landing this requires improving something else, otherwise tests would
regress: we "got lucky" before and did not notice that x >= 0 was
always true when unsigned, so we carried that around, and after x++
it turned into x >= 1 which is no longer trivial. We do need that latter
constraint, so add the following rule: when x++ and x does not
overflow during that addition (as proven by some bound like x < 10)
then we can add x > 0 (since it can no longer be 0 due to the x++).

@kripken
kripken requested a review from tlively August 20, 2026 17:01
@kripken
kripken requested a review from a team as a code owner August 20, 2026 17:01
Comment thread src/ir/constraint.cpp
Comment on lines +45 to +52
if (x <= uint64_t(maxSigned)) {
// This is in the range of both signed and unsigned values, so there is
// no ambiguity. That is, we cannot convert the bit pattern
// 0xffffffff into a Span, as it might be either uint32_t(-1)
// or actually negative (but a bit pattern like 0x00000001 is
// always fine as it can only ever be "1").
return Span<IU64>{x, x};
}

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.

It seems we could be more general here if we separately handled "signed spans" and "unsigned spans" (and disallowed mixing them in any way) rather than trying to smush them both into a single kind of span that handles both signed and unsigned numbers. The ambiguity comes from trying to handle both kinds interchangeably.

Another unambiguous approach would be to do everything in terms of unsigned spans. A signed x < 10 , for instance, could be represented as a pair of unsigned spans: [0, 9], [1 << 31, (1 << 32) - 1].

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.

Yes, those are other options, but I think the common case is handled well by the current code. Most constant upper bounds are within the shared range anyhow (i.e. most fixed-size arrays and such are not of length 2^31+). And, in the unambiguous range, we can support all operations, even mixed (though I admit mixing signed/unsigned might be rare).

Comment thread src/ir/constraint.cpp
Comment on lines +672 to +674
// is impossible after the ++). This is not possible for signed operations,
// since x++ does not prove x > 0 there (0 is not the only value that is
// <= 0).

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.

But we could add x > INT_MIN if the upper bound is signed and proves there is no overflow.

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.

Correct, and if it comes up we could add that I guess. For unsigned, this comes up in every natural loop 0..N (while very few signed loops begin with INT_MIN).

Signed loops also include a lower bound in many cases. Like if a loop begins with an unknown initial value (from a parameter), then an unsigned bounds check is just x < len but a signed one must be x >= 0 && x < len (in unsigned the x >= 0 is "free")

Comment thread src/ir/constraint.cpp Outdated
// since x++ does not prove x > 0 there (0 is not the only value that is
// <= 0).
bool hasUnsignedUpperBound = false;
bool hasUnsignedLowerBound = false;

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.

We never set hasUnsignedLowerBound after this. Is it a bug, or is hasUnsignedLowerBound unnecessary? I guess it's not necessary because if there is an existing lower bound, it will subsume the new x > 0 bound.

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.

Good catch, I had thought to optimize compile time a bit here, but it was incomplete 😄

Yes, it works either way, as the bounds subsume.

I just removed this, as the speedup is probably not worth the code, better to be simple.

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