Eliminate bound checks for "% arr.Length" when possible#130156
Eliminate bound checks for "% arr.Length" when possible#130156henriquewr wants to merge 23 commits into
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
| return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, rightLimit)); | ||
| } | ||
|
|
||
| return Range(Limit(Limit::keConstant, 0), Limit(Limit::keUnknown)); |
There was a problem hiding this comment.
Is there a way of representing the maximum value possible? (limit is int32 in the ctor)
Im not sure how ranges above 32 bits are representable
There was a problem hiding this comment.
Rangecheck is 32bit limited.
|
CI failures are related |
| } | ||
|
|
||
| // "CheckedBnd <relop> X" | ||
| if (!isUnsignedRelop && vnStore->IsVNCheckedBound(op1VN)) |
There was a problem hiding this comment.
As far as I know adding asserts isn't free
these asserts might be taking the place of a more useful one ((uint)index <= (uint)arr.Length and (uint)arr.Length >= (uint)index)
Is doing this really a good idea?
(Also, I don't really know how often the asserts table gets full)
|
TBH, I am not sure I understand what you're doing, or rather this looks like a typical AI contribution given how AI loves to solve problems b in the jit codebase by introducing new loops over entites vs integrating into existing structures. I don't think "arr.Length % X" makes any sense in real code, "X % arrLen" does make sense, and it's supported but only for unsigned mods, it seems it should be possible to extend that logic to signed mods as well with the help of assertions, should be a very trivial change, not +300 LOC of very risky changes in an area that has a poor test coverage. |
I could be wrong, but i think you are talking about the (ugly) while loops, as far as I searched in the codebase, thats the only way of knowing if an assert is know to be applicable
Agree, i dont really think thats the mosty useful thing, but since signed operators already has all the assertions needed (<=, >=, >, <) I just implemented anyway
yeah, most of the code is the ugly loop for assertions to support the not so useful |
|
Workflow state for the Holistic Review Orchestrator. {
"version": 5,
"last_dispatched_commit": "1fcf6f239d9cfcff26376f931ebf20574204020c",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "d03f6b9564cc85413a7cfaa000faeabb82abea47",
"last_reviewed_commit": "1fcf6f239d9cfcff26376f931ebf20574204020c",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "d03f6b9564cc85413a7cfaa000faeabb82abea47",
"last_recorded_worker_run_id": "29695950482",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "c9fea9f8fec1cc3f0d7812ba6af9d96d01fe58bb",
"review_id": 4730676952
},
{
"commit": "1fcf6f239d9cfcff26376f931ebf20574204020c",
"review_id": 4731159221
}
]
} |
There was a problem hiding this comment.
Holistic Review
Motivation: Fixes #130155. The JIT already eliminates the array bounds check for the arr[X u% arr.Length] (unsigned remainder) pattern, since an unsigned remainder by the length is always in [0, arr.Length). This PR extends the same optimization to the signed remainder pattern arr[X % arr.Length] when the index X is provably non-negative, which is a very common idiom (e.g. after an if (index >= 0) guard). The generated code drops the redundant CORINFO_HELP_RNGCHKFAIL path.
Approach: Three coordinated changes: (1) optAssertionProp_BndsChk gains an else if branch matching VNF_MOD(X, arr.Length); it computes the range of X and drops the check when the lower limit is a known constant >= 0. This is safe because for non-negative X, X % len lies in [0, len), and the len == 0 case still throws DivideByZeroException before any access. (2) VNF_MOD is added to the range-propagation switch in RangeCheck::GetRangeFromAssertionsWorker, dispatching to a new RangeOps::Mod. (3) RangeOps::Mod computes a conservative signed-remainder range, handling single-value constants, constant divisor ranges (using abs(divisor)-1 with INT32_MIN guarding), and sign-based bounds for the dividend. The test file adds signed/unsigned variants covering >, >=, <, <= guards and DivideByZeroException cases. The run.py change makes XML time parsing tolerant of comma decimal separators produced under non-invariant cultures.
Summary: The optimization is well-scoped and mirrors the existing, proven VNF_UMOD handling, with the crucial added guard that the dividend is known non-negative — necessary because signed % can yield negative results for negative dividends. The RangeOps::Mod implementation is conservative and the INT32_MIN overflow in abs() is explicitly guarded via SafeAbsMinusOne. The added tests exercise the relevant guard shapes and the div-by-zero behavior. Note that per the commit history the more speculative arr.Length % index support was removed, narrowing the change to the safe, common case. Only a minor cosmetic nit (stray indentation on the [Fact] attribute) was found; no correctness concerns. Verdict: LGTM. Note this worker cannot run builds/tests; test behavior was assessed by reading, and CI status should be consulted for the earlier flagged failures.
Detailed Findings
src/tests/JIT/opt/RangeChecks/ModLength.cs(line 10): cosmetic — stray extra indentation was introduced on the[Fact]attribute. Flagged inline.
No actionable correctness or performance issues were identified in the changed lines. The -0 literals in RangeOps::Mod (e.g. rightLimit = ... ? -0 : ...) are equivalent to 0 for int and harmless, but reading as 0 would be marginally clearer.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 134.7 AIC · ⌖ 10.7 AIC · ⊞ 10K
There was a problem hiding this comment.
Holistic Review
Motivation: Fixes #130155. Extends the JIT bounds-check elimination that already applies to the unsigned remainder pattern arr[X u% arr.Length] to the signed remainder pattern arr[X % arr.Length] when the index X is provably non-negative — a common idiom after an if (index >= 0) guard — dropping the redundant CORINFO_HELP_RNGCHKFAIL path.
Approach: Three coordinated changes: (1) optAssertionProp_BndsChk gains an else if branch matching VNF_MOD(X, arr.Length), computing the range of X and dropping the check when its lower limit is a known constant >= 0 (safe because for non-negative X, X % len lies in [0, len), and len == 0 still throws DivideByZeroException before any access). (2) VNF_MOD is added to the range-propagation switch, dispatching to a new RangeOps::Mod. (3) RangeOps::Mod computes a conservative signed-remainder range with INT32_MIN guarded via SafeAbsMinusOne. Tests cover signed/unsigned variants across >, >=, <, <= guards and div-by-zero cases; a run.py change makes XML time parsing tolerant of comma decimal separators. The single incremental commit since the prior review (1fcf6f2, "Wrong identation") fixes the stray [Fact] indentation flagged previously.
Summary: The incremental change corrects the one cosmetic nit identified in the prior review, restoring correct indentation on the [Fact] attribute in ModLength.cs. No functional code changed since the last review; the optimization remains well-scoped, mirrors the proven VNF_UMOD handling, and carries the crucial non-negative-dividend guard. No new correctness or performance concerns in the incremental scope. Verdict: LGTM. This worker cannot run builds/tests; test behavior was assessed by reading and CI status should be consulted.
Assessment History:
- review 4730676952 (commit
c9fea9f): verdict LGTM with one cosmetic indentation nit. Current verdict: LGTM. Changed — the sole flagged nit is now resolved by commit1fcf6f2; motivation, approach, and risk are otherwise unchanged.
Detailed Findings
No actionable findings in the incremental scope. The prior cosmetic indentation nit on the [Fact] attribute has been fixed.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 40.6 AIC · ⌖ 9.85 AIC · ⊞ 10K
fixes #130155
Pattern
[index % arr.Length]:went from
to