-
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
JIT: A small CQ improvement for Equals/StartsWith unrolling #76439
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsA quick fix for a CQ problem noticed by @stephentoub: ; Method Program:IsHttps(System.String):bool
G_M40142_IG01:
C5F877 vzeroupper
G_M40142_IG02:
83790808 cmp dword ptr [rcx+08H], 8
7C25 jl SHORT G_M40142_IG04
G_M40142_IG03:
C5F910410C vmovupd xmm0, xmmword ptr [rcx+0CH]
C5F9EB052A000000 vpor xmm0, xmm0, xmmword ptr [reloc @RWD00]
C5F9EF0532000000 vpxor xmm0, xmm0, xmmword ptr [reloc @RWD16]
C4E27917C0 vptest xmm0, xmm0
0F94C0 sete al
0FB6C0 movzx rax, al
EB02 jmp SHORT G_M40142_IG05
G_M40142_IG04:
33C0 xor eax, eax
G_M40142_IG05:
- 0FB6C0 movzx rax, al
-G_M40142_IG06:
C3 ret
RWD00 dq 0020002000200020h, 0000000000000020h
RWD16 dq 0070007400740068h, 002F002F003A0073h
; Total bytes of code: 49
|
a2d5830
to
a213f62
Compare
@dotnet/jit-contrib PTAL, single-line change with nice diffs diff example: https://www.diffchecker.com/uJZJ51cv |
@@ -526,15 +526,15 @@ GenTree* Compiler::impExpandHalfConstEquals(GenTreeLclVar* data, | |||
GenTreeColon* lenCheckColon = gtNewColonNode(TYP_INT, indirCmp, gtNewFalse()); | |||
|
|||
// For StartsWith we use GT_GE, e.g.: `x.Length >= 10` | |||
lenCheckNode = gtNewQmarkNode(TYP_INT, gtNewOperNode(cmpOp, TYP_INT, lengthFld, elementsCount), lenCheckColon); | |||
lenCheckNode = gtNewQmarkNode(TYP_BOOL, gtNewOperNode(cmpOp, TYP_INT, lengthFld, elementsCount), lenCheckColon); |
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.
Is this a standard IR shape for qmarks? TYP_BOOL
typed node is quite uncommon in the JIT IR.
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.
What ends up happening to the qmark when it is bool typed? How does this change the IR post qmark rationalization?
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.
I assume they're pretty legal till Lower, in this case we get rid of QMARK in morph always
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.
@jakobbotsch DIFF for the snippet in the description: https://www.diffchecker.com/QeDP5n5k (Main vs PR)
as you can see it creates an assertion (value is 0..255) for LCL we spill QMARK to. Then that assertion helps to get rid of CAST node.
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.
It would be interesting to see if there would be even better diffs by generalizing IntegralRange::ForNode
slightly instead to handle qmarks (by union, I guess). Maybe commas too. From cursory glance the subrange assertion is coming from that.
This change is probably ok but it does look strange to have TYP_BOOL
qmark with TYP_INT
relop as 'then' branch, but I suppose we know that relops always leave the upper 31 bits cleared.
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.
@jakobbotsch hm, let's see what CI thinks about it
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.
Changed to be handled in IntegralRange::ForNode
- no new diffs so far, so it's just a more verbose change now 🙂 maybe will help some future code (if we won't get rid of QMARK at that point yet)
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.
I wouldn't expect it for qmark, but maybe if you also handle commas 🙂
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.
I wouldn't expect it for qmark, but maybe if you also handle commas 🙂
No new diffs (for libraries.pmi)
FWIW, I am increasingly thinking we should get rid of qmarks immediately after importation. So would rather not see us build in cleverness that would have to be compensated for elsewhere. Can't local assertion prop get this (if suitably enhanced?) |
Seems like it would be difficult to get this with local assertion prop after qmark rationalization, since the assignments to the local and the cast being eliminated would be in different basic blocks. But maybe global assertion prop could be enhanced to remove some of these casts. |
I thought qmark expansion was done after morph? In which case local prop would not be hindered by control flow, but the qmark assertion reconciliation might need some enhancing. |
And just to show I don't often follow my own advice -- I am experimenting with allowing forward sub to work on qmarks.... similar to the ideas you had for modifying importation of isinst if we know it is feeding a compare. |
What I mean is if we remove/early expand qmarks in the future then local assertion prop is going to stop being able to recognize this pattern in a quite fundamental manner. FWIW, the current change in |
Right. But we really ought to teach morph to walk the blocks in reverse postorder and propagate facts forward cross-block when it can, it ought not to cost all that much... (?). I tried this for the simple case where the block falls through and has a non-join bbNext and got some nice diffs but also some annoying regressions. |
Amusingly enough my change this is hitting a very similar kind of issue, morph is putting a subrange cast over a qmark
|
Does it look good now? |
Interesting that this leads to diffs with minopts. |
Diffs (just x64 asp.net) of your change merged onto mine looks exactly like your standalone asp.net diffs. So, no overlap there at least. Diffs are based on 69,704 contexts (36,907 MinOpts, 32,797 FullOpts). MISSED contexts: base: 22, diff: 22 Overall (-3,079 bytes)
MinOpts (-944 bytes)
FullOpts (-2,135 bytes)
Details
jit-analyze outputaspnet.run.windows.x64.checked.mchTo reproduce these diffs on Windows x64:
Detail diffs
|
Looks like Morph still performs transformations without OptimizationsEnabled checks, we might want to audit all transformations there |
A quick fix for a CQ problem noticed by @stephentoub:
Some nice diffs.