Skip to content

JIT: Range Checks for "(uint)i > cns" pattern - #62864

Merged
EgorBo merged 34 commits into
dotnet:mainfrom
EgorBo:bound-checks-constant-len
Feb 25, 2022
Merged

JIT: Range Checks for "(uint)i > cns" pattern#62864
EgorBo merged 34 commits into
dotnet:mainfrom
EgorBo:bound-checks-constant-len

Conversation

@EgorBo

@EgorBoEgorBo commented Dec 15, 2021

Copy link
Copy Markdown
Member

Closes#13464
Closes#1343

Example:

staticReadOnlySpan<byte>MySpan=>newbyte[]{1,2,3,4,5};intGetElement(intindex){if((uint)index<(uint)MySpan.Length)// and all variationsreturnMySpan[index];return0;}

Codegen diff:

; Method Program:GetElement(int):int:this
G_M56827_IG01:
- sub rsp, 40
G_M56827_IG02:
- mov eax, edx- cmp rax, 5- jge SHORT G_M56827_IG05+ cmp edx, 5+ jae SHORT G_M56827_IG05
G_M56827_IG03:
- cmp edx, 5- jae SHORT G_M56827_IG07
mov eax, edx
mov rdx, 0xD1FFAB1E
movzx rax, byte ptr [rax+rdx]
G_M56827_IG04:
- add rsp, 40
ret G_M56827_IG05:
xor eax, eax
G_M56827_IG06:
- add rsp, 40
ret -G_M56827_IG07:- call CORINFO_HELP_RNGCHKFAIL- int3 -; Total bytes of code: 51+; Total bytes of code: 25

(uint) cast is still needed for index because it can be negative. Alternative pattern index >= 0 && index < MySpan.Length also works here.

For all of these variations bound checks are now eliminated (none of them are eliminated in .NET 6.0):

publicclassTests{staticReadOnlySpan<byte>MySpan=>newbyte[]{1,2,3,4,5};publicstaticintTest1(intindex){if((uint)index<MySpan.Length)returnMySpan[index];return0;}publicstaticintTest2(intindex){if(MySpan.Length>(uint)index)returnMySpan[index];return0;}publicstaticintTest3(intindex){if(MySpan.Length<=(uint)index)return0;returnMySpan[index];}publicstaticintTest4(intindex){if((uint)index>=MySpan.Length)return0;returnMySpan[index];}publicstaticintTest5(intindex){if(index<0||index>=MySpan.Length)return0;returnMySpan[index];}publicstaticintTest6(intindex){if(index>=0&&index<MySpan.Length)returnMySpan[index];return0;}publicstaticintTest7(intindex){if(index<0)thrownewException();if(index>=MySpan.Length)thrownewArgumentException();returnMySpan[index];}publicstaticintTest8(intindex){if((uint)index<2)// 2 is less than MySpan.LengthreturnMySpan[index];return0;}publicstaticintTest9(intindex){if(index<2||index>4)// e.g. we restrict index to be in [2..3] range despite the fact MySpan is fine with [0..4]thrownewArgumentException();returnMySpan[index];}}

Unrelated pattern that got fixed too:

intfoo(uinti,int[]array){if(i<array.Length)returnarray[i];return0;}

if index is defined as unsigned - we don't need any casts to uint.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 15, 2021
@ghostghost assigned EgorBoDec 15, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @JulieLeeMSFT
See info in area-owners.md if you want to be subscribed.

Issue Details

Closes #13464
Closes #1343

Example:

staticReadOnlySpan<byte>MySpan=>newbyte[]{1,2,3,4,5};intGetElement(intindex){if((uint)index<MySpan.Length)returnMySpan[index];return0;}

Codegen diff:

; Method Program:GetElement(int):int:this
G_M56827_IG01:
- sub rsp, 40
G_M56827_IG02:
- mov eax, edx- cmp rax, 5- jge SHORT G_M56827_IG05+ cmp edx, 5+ jae SHORT G_M56827_IG05
G_M56827_IG03:
- cmp edx, 5- jae SHORT G_M56827_IG07
mov eax, edx
mov rdx, 0xD1FFAB1E
movzx rax, byte ptr [rax+rdx]
G_M56827_IG04:
- add rsp, 40
ret G_M56827_IG05:
xor eax, eax
G_M56827_IG06:
- add rsp, 40
ret -G_M56827_IG07:- call CORINFO_HELP_RNGCHKFAIL- int3 -; Total bytes of code: 51+; Total bytes of code: 25
Author:EgorBo
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@EgorBo

EgorBo commented Dec 15, 2021

Copy link
Copy Markdown
MemberAuthor

As a side bonus it removes bound checks for this:

staticintFoo(int[]array,inti){if((uint)i<array.Length)returnarray[i];return0;}

Previously it needed(uint) cast for array.Length too. But it won't work if array is Span yet (but I am planning to handle it at some point - e.g. #62421 adds [Intrinsic] for Span.get_Length())

@EgorBo

EgorBo commented Dec 17, 2021

Copy link
Copy Markdown
MemberAuthor

For all of these variations bound checks are now eliminated (none of them are eliminated in .NET 6.0):

publicclassTests{staticReadOnlySpan<byte>MySpan=>newbyte[]{1,2,3,4,5};publicstaticintTest1(intindex){if((uint)index<MySpan.Length)returnMySpan[index];return0;}publicstaticintTest2(intindex){if(MySpan.Length>(uint)index)returnMySpan[index];return0;}publicstaticintTest3(intindex){if(MySpan.Length<=(uint)index)return0;returnMySpan[index];}publicstaticintTest4(intindex){if((uint)index>=MySpan.Length)return0;returnMySpan[index];}publicstaticintTest5(intindex){if(index<0||index>=MySpan.Length)return0;returnMySpan[index];}publicstaticintTest6(intindex){if(index>=0&&index<MySpan.Length)returnMySpan[index];return0;}publicstaticintTest7(intindex){if(index<0)thrownewException();if(index>=MySpan.Length)thrownewArgumentException();returnMySpan[index];}publicstaticintTest8(intindex){if((uint)index<2)// 2 is less than MySpan.LengthreturnMySpan[index];return0;}publicstaticintTest9(intindex){if(index<2||index>4)// e.g. we restrict index to be in [2..3] range despite the fact MySpan is fine with [0..4]thrownewArgumentException();returnMySpan[index];}}

Unrelated pattern that got fixed too:

intfoo(uinti,int[]array){if(i<array.Length)returnarray[i];return0;}

if index is defined as unsigned - we don't need any casts to uint.

@EgorBo
EgorBoforce-pushed the bound-checks-constant-len branch from e87d3b2 to 58f4f68CompareDecember 18, 2021 00:31
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/azp run Fuzzlyn, Antigen

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@EgorBo
EgorBo marked this pull request as ready for review December 20, 2021 10:47
@EgorBo

EgorBo commented Dec 20, 2021

Copy link
Copy Markdown
MemberAuthor

@dotnet/jit-contrib @SingleAccretion PTAL (Antigen failures are not related)

I generated a lot of test cases locally (~100k lines of code) e.g. https://gist.github.com/EgorBo/1ba16f0d9b1b7d87044c7ed4d14db117 to validate there are no surprises. But I don't think I should add them to the repo as they are too big/slow to run and don't add much value.

@EgorBo

EgorBo commented Dec 23, 2021

Copy link
Copy Markdown
MemberAuthor

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@dotnet/jit-contrib PTAL

Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/valuenum.h Outdated
@EgorBo

EgorBo commented Feb 23, 2022

Copy link
Copy Markdown
MemberAuthor

@jakobbotsch can you take a look again? New diffs: https://dev.azure.com/dnceng/public/_build/results?buildId=1621068&view=ms.vss-build-web.run-extensions-tab

I inspected a couple of minor regressions and they seem to be caused by CSE/RA

@jakobbotschjakobbotsch left a comment

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.

LGTM, just had one question.
cc @SingleAccretion in case you want to give this a lookover as well.

Comment threadsrc/coreclr/jit/morph.cpp
Comment threadsrc/coreclr/jit/morph.cpp
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/morph.cpp Outdated
EgorBoand others added 3 commits February 24, 2022 23:46
Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
@EgorBo

Copy link
Copy Markdown
MemberAuthor

@SingleAccretion thanks for the feedback!! addressed almost all of it except BashToNop - I'm leaving it to whoever will refactor it to optNarrowTree.

Failures are not related: #65818

@EgorBo
EgorBo merged commit e4dde6b into dotnet:mainFeb 25, 2022
@EgorBo
EgorBo deleted the bound-checks-constant-len branch February 25, 2022 17:40
@ghostghost locked as resolved and limited conversation to collaborators Mar 27, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Span indexer doesn't elide bounds check when span initialized from an RVA static JIT doesn't eliminate bounds checks on const strings

4 participants

@EgorBo@jakobbotsch@SingleAccretion@JulieLeeMSFT