Repository navigation
Modular range folds - #136994
Draft
NinoFloris wants to merge 9 commits into
Draft
Modular range folds#136994NinoFloris wants to merge 9 commits into
NinoFloris wants to merge 9 commits into
Conversation
Move the modular interval arithmetic of redundant branch opts into modularrange.h, a ring interval that models constant comparisons of either signedness, with symbolic upper bounds by identity. The redundant branch optimizer is converted to construct and intersect these intervals; it produces no code differences.
Replace GetIntersection, FoldRangeTests and FoldNeverNegativeRangeTest with a single FoldComparisons that intersects the two tests as modular intervals and emits the comparison form that matches the result. The conversion keeps the previously accepted shapes only: two tests of the same signedness on a plain local that enclose a range of at least two non-negative values, or "X >= 0" paired with a never-negative symbolic bound. It produces no code differences. The shapes the interval representation makes available, equality tests, negative bounds, offset bases and single values, are enabled separately.
…ools Accept equality tests, offset bases, field locals, mixed signedness, overlapping ranges and single values in the range test fold. The dominant new shape is "X != a && X != a + 1", which becomes a single unsigned comparison.
The previous fold rejected negative constants. On the modular interval they are ordinary points, so "lo <= X <= hi" with a negative lower bound becomes "(X - lo) u<= hi - lo" like any other range.
SUB/CMP needs as many arithmetic instructions as CMP/CCMP. Decline the offset form of a fold when its constants cost more than the two original bounds.
|
Azure Pipelines: Successfully started running 6 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
NinoFloris
force-pushed
the
jit/modular-range-folds
branch
from
November 5, 2026 04:13
0aaaeab to
166e5b7
Compare
The general case of the range fold lowered an interval as "(X - lower) u<= width" whatever its position on the ring. An interval that covers more than half of the ring is the complement of a small one, and one that ends near the top of the signed range is the signed test of its offset value, so the literal encoding materialized constants like 0xFFFFFFFD for "x != a && x != a + 1" and 0x7FFFFFF9 for "(uint)x < 257 || (x - 257) < 6". On arm64 the wrapped bound still encodes in a cmn while the compare stands alone, but once the compare joins a ccmp chain its 5-bit immediate forces a movn, which is where the folds grew code. An interval has four encodings: the unsigned test of itself or of its complement, and the signed test of either, which starts the interval at the minimum value. They differ only in their constants. Price each with the cost model and take the cheapest, with the smallest constants on a tie, since those fit the most immediate forms. The two shapes above become "(x - a) u> 1" and "(x - 257) <s 6".
…ptors The value number store and the assertion table are released after the SSA based phases, so a later phase that asks GenTree::IsNeverNegative about a local only sees the flag on its descriptor, which import and promotion set for span lengths and little else. A length read into a plain local is never negative by value number and nothing after value numbering could tell. Add a phase at the end of the SSA section that writes such facts onto the descriptors. The first is never-negative: a local in SSA whose every definition is never negative by value number gets the flag, where the implicit initial definition counts as zero when the method zero-initializes its locals and disqualifies the local otherwise. Optimize bools, if conversion, lowering and codegen then see array and span lengths, checked bounds and the temps CSE made for them the way the value number phases did.
A value that is never negative takes only the nonnegative half of the ring, but the range fold intersected its tests over the whole ring, so a pair like "(uint)length < 257 || (length - 257) < 6" on an array length produced an interval straddling the sign boundary and a signed test with an offset. Intersect the first test's interval with the nonnegative half when the value is never negative; the pair above is then "length < 263" with no arithmetic.
NinoFloris
force-pushed
the
jit/modular-range-folds
branch
from
November 5, 2026 04:24
166e5b7 to
61d264e
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to, and based on, #136926. Only the last 4 commits are relevant: 18 lines added and 31 removed.