-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Span bounds check elision doesn't work properly when method calls occur between bounds check and access #49113
Comments
A quick glance at the trees suggests that this is the result of inlining breaking them up. ***** BB03
STMT00003 (IL 0x009...0x016)
N008 ( 16, 16) [000023] -A-XG------- * ASG int $VN.Void
N006 ( 14, 14) [000020] ---XG--N---- +--* COMMA int $VN.Void
N003 ( 8, 10) [000015] ---XG------- | +--* ARR_BOUNDS_CHECK_Rng void <l:$2c5, c:$2c4>
N001 ( 1, 1) [000010] ------------ | | +--* CNS_INT int 0 $40
N002 ( 3, 2) [000014] ------------ | | \--* LCL_VAR int V03 tmp2 u:1 (last use) <l:$200, c:$240>
N005 ( 6, 4) [000047] *--X---N---- | \--* IND int $44
N004 ( 3, 2) [000018] ------------ | \--* LCL_VAR byref V02 tmp1 u:1 (last use) <l:$100, c:$140>
N007 ( 1, 1) [000021] ------------ \--* CNS_INT int 42 $44 This is for the method case: ------------ BB03 [009..017), preds={BB02} succs={BB04}
***** BB03
STMT00004 (IL 0x009...0x016)
N003 ( 8, 10) [000015] ---XG------- * ARR_BOUNDS_CHECK_Rng void <l:$2c5, c:$2c4>
N001 ( 1, 1) [000010] ------------ +--* CNS_INT int 0 $40
N002 ( 3, 2) [000014] ------------ \--* LCL_VAR int V04 tmp3 u:1 (last use) <l:$200, c:$240>
***** BB03
STMT00005 (IL ???... ???)
N004 ( 8, 6) [000027] -A-XG------- * ASG int $VN.Void
N002 ( 6, 4) [000026] *--X---N---- +--* IND int $44
N001 ( 3, 2) [000024] ------------ | \--* LCL_VAR byref V03 tmp2 u:1 (last use) <l:$100, c:$140>
N003 ( 1, 1) [000036] ------------ \--* CNS_INT int 42 $44 A source-level workaround would be the use of a local: if (!destination.IsEmpty)
{
var tmp = SomeMethod();
destination[0] = tmp;
} A Jit-level fix could be stitching these trees together (fairly early) or recognizing them where relevant. A few phases remove checks right now, so stitching would be more effective. It would also be a more complex solution (the early trees are fairly complex). An easier solution would be to update assertion propagation to understand this case: runtime/src/coreclr/jit/assertionprop.cpp Lines 4066 to 4068 in bf3789d
The flag is already being set for the problematic case, but the machinery to act on it is missing, as pretty much everything assumes bounds checks are GT_COMMA -based.
|
CC @AndyAyersMS |
@SingleAccretion's analysis is spot on. We break trees for inline candidates and don't glue them back, and the bounds check elimination that happens later depends on intact trees. We can either not break trees in the first place, break them and then re-form them (forward substitution #6973), or update subsequent optimizations to be more tolerant of broken trees. |
Seems like if we passed I don't know for sure if this would catch all the cases but seems like it might; the bounds check doesn't return a value so can't be incorporated into trees other than within commas. @SingleAccretion want to give this a try? |
Yep, and I already have a version of the proposed approach working locally. It needs some cleanup and the addition of the same handling in |
Excellent. I have assigned this issue to you. |
If the caller performs a bounds check on a span, the runtime will not elide future bounds checks satisfied by that condition if a method call occurs between the original check and the subsequent access. The following repro code demonstrates this issue.
x64 codegen, courtesy SharpLab:
The text was updated successfully, but these errors were encountered: