-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT: Add support for bounds check no throw assertions in range check and make overflow check more precise #100777
Conversation
This PR in its current state still doesn't fix the duplicated bounds checks in the shared generic version of |
For some reason, the Merging assertions from pred edges of BB07 for op [000074] $401
Computed Range [000074] => <0, $346 + -1>
}
Does overflow [000074]?
Does overflow [000526]?
Does overflow [000536]?
Does overflow [000085]?
[000085] does not overflow
[000536] does not overflow
Does overflow [000534]?
Does overflow [000054]?
Does overflow [000052]?
[000052] does not overflow
Does overflow [000053]?
[000053] does not overflow
Checking bin op overflow ADD <Unknown, Unknown> <-1, -1>
[000054] overflows
[000534] overflows
[000526] overflows
[000074] overflows
Method determined to overflow. No idea what range check is trying to do here, why is this "can overflow" check even necessary if we have proved a range through other means? I suppose range check internally reasons without considering overflow, then later only uses the result if it can prove no overflow is possible. But clearly that's conservative here. A new minimal repro that does not manage to get rid of bounds checks looks like: [MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(ref int startIndex, int[] indices)
{
int i = startIndex - 1;
while ((uint)i < (uint)indices.Length)
{
i = indices[i];
}
return i;
} |
Yep, it's not the first time I see "CanOverflow" ruining things becuase of being too conservative 🤷 |
|
Not quite. Even if we have an assertion for a particular local's value we still may have computed a tighter range based on the local's definition, so if that computation didn't take into account the possibility of overflow then we still need to examine its def. On a side note |
I remember I tried that in the past. I think we even discussed possible cases why it currently cannot be removed, probably @SingleAccretion remembers |
/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 3 pipeline(s). |
cc @dotnet/jit-contrib PTAL @AndyAyersMS @EgorBo Diffs. A few minor regressions which happen because using this new assertion means we may end up computing a range that gives an upper bound in terms of a different array. That might cause us to no longer eliminate some bounds checks on other arrays. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me
I found this from the discussion we had in the past about it: SingleAccretion — 03/29/2023 9:25 AM |
Thanks for pointing to that! I'll add a test for something like that to #100820 so we at least have some coverage. So it seems like the overflow handling during the range computation just isn't complete. This example computes the following: BinOp add ranges <Dependent, $c3 + -1> <2, 2> = <Dependent, $c3 + 1>
Computed Range [000030] => <Dependent, $c3 + 1> I think this should rather compute to |
FWIW, I want to study the example above a bit more carefully before I merge this one. |
I think it's fine. The range returned by range check without the overflow check may have an incorrect lower bound for the monotonically increasing cases; but assertions are sufficient on those paths to make the lower bound right anyway. |
…and make overflow check more precise (dotnet#100777) Fix dotnet#9422 Fix dotnet#83349
Fix #9422
Fix #83349
Example:
Before:
After: