Skip to content
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: Import string.Empty as "" #64530

Merged
merged 10 commits into from
Feb 3, 2022
Merged

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Jan 31, 2022

A better version of #44847, it now saves mdToken of actual "" in VM and JIT just asks VM for it.
Extends effect of #63734 for string.Empty

Example:

int Test1() => string.Empty.Length; // e.g. after inlining

bool Test2(string s) => s == string.Empty; // is string empty?

Current codegen:

; Method Tests:Test1():int:this
G_M28758_IG01:
G_M28758_IG02:
       mov      rax, 0xD1FFAB1E      ; ""
       mov      rax, gword ptr [rax]
       mov      eax, dword ptr [rax+8]
G_M28758_IG03:
       ret      
; Total bytes of code: 17


; Method Tests:Test2(System.String):bool:this
G_M31606_IG01:
       push     rax
G_M31606_IG02:
       mov      r8, 0xD1FFAB1E      ; ""
       mov      r8, gword ptr [r8]
       cmp      rdx, r8
       jne      SHORT G_M31606_IG04
G_M31606_IG03:
       mov      eax, 1
       jmp      SHORT G_M31606_IG08
G_M31606_IG04:
       test     rdx, rdx
       je       SHORT G_M31606_IG05
       mov      ecx, dword ptr [rdx+8]
       cmp      ecx, dword ptr [r8+8]
       je       SHORT G_M31606_IG06
G_M31606_IG05:
       xor      eax, eax
       jmp      SHORT G_M31606_IG08
G_M31606_IG06:
       mov      eax, dword ptr [r8+8]
       add      rdx, 12
       add      r8, 12
       mov      bword ptr [rsp], r8
       add      ecx, ecx
       mov      r8d, ecx
       mov      rcx, rdx
       mov      rdx, bword ptr [rsp]
G_M31606_IG07:
       add      rsp, 8
       jmp      System.SpanHelpers:SequenceEqual(byref,byref,long):bool
G_M31606_IG08:
       add      rsp, 8
       ret      
; Total bytes of code: 86

New codegen:

; Method Tests:Test1():int:this
G_M28758_IG01:
G_M28758_IG02:
       xor      eax, eax
G_M28758_IG03:
       ret      
; Total bytes of code: 3


; Method Tests:Test2(System.String):bool:this
G_M31606_IG01:
G_M31606_IG02:
       test     rdx, rdx
       je       SHORT G_M31606_IG04
G_M31606_IG03:
       cmp      dword ptr [rdx+8], 0
       sete     al
       movzx    rax, al
       jmp      SHORT G_M31606_IG05
G_M31606_IG04:
       xor      eax, eax
G_M31606_IG05:
       ret      
; Total bytes of code: 20

Closes #42694

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jan 31, 2022
@ghost ghost assigned EgorBo Jan 31, 2022
@ghost
Copy link

ghost commented Jan 31, 2022

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

Issue Details

A better version of #44847, it now saves an mdToken of actual "" in VM and JIT just asks VM for it.
Extends effect of #63734 for string.Empty

Example:

int Test1() => string.Empty.Length;

bool Test2(string s) => s == string.Empty;

Current codegen:

; Method Tests:Test1():int:this
G_M28758_IG01:
G_M28758_IG02:
       mov      rax, 0xD1FFAB1E      ; ""
       mov      rax, gword ptr [rax]
       mov      eax, dword ptr [rax+8]
G_M28758_IG03:
       ret      
; Total bytes of code: 17


; Method Tests:Test2(System.String):bool:this
G_M31606_IG01:
       push     rax
G_M31606_IG02:
       mov      r8, 0xD1FFAB1E      ; ""
       mov      r8, gword ptr [r8]
       cmp      rdx, r8
       jne      SHORT G_M31606_IG04
G_M31606_IG03:
       mov      eax, 1
       jmp      SHORT G_M31606_IG08
G_M31606_IG04:
       test     rdx, rdx
       je       SHORT G_M31606_IG05
       mov      ecx, dword ptr [rdx+8]
       cmp      ecx, dword ptr [r8+8]
       je       SHORT G_M31606_IG06
G_M31606_IG05:
       xor      eax, eax
       jmp      SHORT G_M31606_IG08
G_M31606_IG06:
       mov      eax, dword ptr [r8+8]
       add      rdx, 12
       add      r8, 12
       mov      bword ptr [rsp], r8
       add      ecx, ecx
       mov      r8d, ecx
       mov      rcx, rdx
       mov      rdx, bword ptr [rsp]
G_M31606_IG07:
       add      rsp, 8
       jmp      System.SpanHelpers:SequenceEqual(byref,byref,long):bool
G_M31606_IG08:
       add      rsp, 8
       ret      
; Total bytes of code: 86

New codegen:

; Method Tests:Test1():int:this
G_M28758_IG01:
G_M28758_IG02:
       xor      eax, eax
G_M28758_IG03:
       ret      
; Total bytes of code: 3


; Method Tests:Test2(System.String):bool:this
G_M31606_IG01:
G_M31606_IG02:
       test     rdx, rdx
       je       SHORT G_M31606_IG04
G_M31606_IG03:
       cmp      dword ptr [rdx+8], 0
       sete     al
       movzx    rax, al
       jmp      SHORT G_M31606_IG05
G_M31606_IG04:
       xor      eax, eax
G_M31606_IG05:
       ret      
; Total bytes of code: 20

Closes #42694

Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@am11
Copy link
Member

am11 commented Jan 31, 2022

; Method Tests:Test2(System.String):bool:this

Would be cooler if we could eliminate extra move and jump like native compilers do: https://godbolt.org/z/7r7fbr9Y6, but this is great work as-is, thanks! 👍

@jkotas
Copy link
Member

jkotas commented Jan 31, 2022

Set module to corelib, just in case

What's the point of this change? I think it can only mask real problems.

@EgorBo
Copy link
Member Author

EgorBo commented Jan 31, 2022

Set module to corelib, just in case

What's the point of this change? I think it can only mask real problems.

You're right, if anyone introduces a new EE-JIT api to work with strings or use any of the existing ones in a new place - it should crash if not guarded with if (IsStringEmptyField())

@EgorBo
Copy link
Member Author

EgorBo commented Jan 31, 2022

PTAL @dotnet/jit-contrib

Diffs: https://dev.azure.com/dnceng/public/_build/results?buildId=1582186&view=ms.vss-build-web.run-extensions-tab
(not sure it's correct, running locally, the CI job might picked up a wrong baseline)

From a local run I see a lot of size regressions due to inlining (along with improvements). Now, when the inliner sees a GT_CNS_STR it considers it as a constant argument so all operations on this argument in callees are considered foldable including branches leading to bigger "benefit multipliers". Example: https://www.diffchecker.com/mLb1tijN (see

)

Unfortunately, there is a faulty (too optimistic) heuristic "if I see an unknown call and we have a const argument on stack - the call most likely returns another const" - I am going to remove it, it's responsible for huge diffs in general.

src/coreclr/jit/gentree.cpp Outdated Show resolved Hide resolved
@EgorBo EgorBo merged commit 59662ad into dotnet:main Feb 3, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Mar 5, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR 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.

RyuJIT: String.Empty vs ""
5 participants