-
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
Add simple Else conditions to If Conversion #77728
Add simple Else conditions to If Conversion #77728
Conversation
For example: if (x < 7) { a = 5; } else { a = 9; } a = (cond) ? b : c; The else condition must write to the same variable as the then statement.
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsFor example: The else condition must write to the same variable as the then statement. This patch builds on top of #73472 It refactors a lot of the code in the If Conversion pass. Various parts are split into new functions. Following the style of other passes, I put everything inside it's own OptIfConversionDsc class too. Detection is slightly reworked - It now finds a valid flow and then checks all the statements inside that flow are valid. I think this is slightly better for performance. The existing if conversion logic should be unchanged - all the checks and modifications are the same. This does not catch GT_RETURN cases, for example:
|
Note, due to #77723, after rebasing on top of the latest head, I was no longer able to build anything. All changes are contained within optIfConvert, so I'm not expecting any issues due to the rebase. |
The way I've done the SSA feels a little like a fudge, but I wasn't sure how else to do it. Before optimisation we have two assignments and one jtrue. This gives us two SSA definitions
After the optimisation, we now just have a single assignment:
But, we need two SSA definitions. Especially so given that the next block will probably be a PHI.
This feels wrong, but the second assignment is eventually optimised away to nothing. The alternative would be to find all uses of SSA DEF 2 across all blocks and update them to use SSA DEF 1 ? Then somehow remove SSA DEF 2 ? |
Since these are currently not possible - move the phase to run after range check and declare it to invalidate SSA? |
@dotnet/jit-contrib |
I tried moving optIfConversion to be after optOptimizeBools and removing all my SSA fix ups (included the ones in HEAD). With that I get no test failures. Does the SSA state not matter after a certain point? Is there something specific I should be calling to state that the SSA is invalid? |
Yes, after the last phase that uses it. Currently this is range check.
No, a comment in |
I've been having a quick look at GT_RETURN. It's going to require a little reworking of the code in the PR, but otherwise looks fairly simple to add in. To avoid people re-reviewing another rewrite of the same bits of code, I was thinking it's probably best to get it ready then merge it with this PR. |
For now you can use |
Some spmidiff results on Arm64 Linux:
Regressions look like just the usual issues around 0s. That should improve once we starting using XZR register for csel. |
I'm happy with this patch now. It catches return cases, eg: One test failure, but that looks like just a spurious build failure. Not happy with the IsPreferredRelop() function, ideally it would use the GenConditionDesc map, but that's in Codegen, and I wasn't sure if was ok be calling into codegen from a front end pass. Essentially, just need to make sure the GenConditionDesc created in genCodeForSelect only requires a single condition (which is now guarded with an assert). |
Sounds great! I'll try to get to this soon. In the meantime I will run some testing.
I assume this is just heuristically, right? The backend should be able to handle |
/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, Fuzzlyn |
Azure Pipelines successfully started running 3 pipeline(s). |
Change-Id: I61c8419d32d10d005c746cc9ad6ee4e89320d015 CustomizedGitHooks: yes
Change-Id: If61e778cc97e57dd20fd36ed623a1b0870c2b2bd CustomizedGitHooks: yes
What's happening here is there is a Later during If Conversion, it hoists the I don't think we can or would want to recalculate boundary checks during if conversion, so the best I can do with the flags available is:
Incidentally, I think boundary checks always have an else statement, which is why it's only coming up in this patch. |
I think the fix should be to make sure we set |
Yes, I think that'd work. Not sure if a failing test case on main is possible - will give it a go. |
Ok, so.... Patch has been rebased after #78698, all review comments should be covered and all the tests are passing. |
/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress |
Azure Pipelines successfully started running 2 pipeline(s). |
I looked at some of the diffs:
-G_M14134_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
- tbnz w0, #0, G_M14134_IG04
- ;; size=4 bbWeight=1 PerfScore 1.00
-G_M14134_IG03: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
- mov w2, #5
- tst w1, #1
- csel w0, w2, w0, eq
- ;; size=12 bbWeight=0.50 PerfScore 0.75
-G_M14134_IG04: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
+G_M14134_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
+ and w2, w0, #1
+ and w3, w1, #1
+ orr w2, w2, w3
+ mov w3, #5
+ cmp w2, #0
+ csel w0, w3, w0, eq Let's keep an eye on this one in the benchmarks.
- ldr x0, [fp, #0x28] // [V20 tmp18]
- ldr w1, [fp, #0x24] // [V19 tmp17]
+ ldr x0, [fp, #0x30] // [V20 tmp18]
+ ldr w1, [fp, #0x2C] // [V19 tmp17]
orr x0, x0, x1
- cbnz x0, G_M58282_IG04
- mov w2, wzr
- b G_M58282_IG08
- ;; size=84 bbWeight=0.50 PerfScore 13.50
-G_M58282_IG04: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
- ldr w2, [fp, #0x20] // [V18 tmp16]
- asr w0, w2, #31
- orr w2, w0, #1
- b G_M58282_IG08
+ ldr w1, [fp, #0x28] // [V18 tmp16]
+ asr w1, w1, #31
+ orr w1, w1, #1
+ cmp x0, #0
+ csel w19, w1, wzr, ne
+ b G_M58282_IG07
+ ;; size=96 bbWeight=0.50 PerfScore 15.00 Looks pretty good, the regression comes from different (worse) register allocation. On the opposite end: -24 (-37.50 % of base) : 3407.dasm - System.Numerics.INumber`1[ushort]:Min(ushort,ushort):ushort
-16 (-36.36 % of base) : 13042.dasm - System.Xml.XmlElement:get_LastNode():System.Xml.XmlLinkedNode:this
-12 (-33.33 % of base) : 2804.dasm - System.Math:Max(int,int):int
-12 (-33.33 % of base) : 2806.dasm - System.Math:Max(uint,uint):uint
-12 (-33.33 % of base) : 2579.dasm - System.Math:Min(int,int):int
-12 (-33.33 % of base) : 2805.dasm - System.Math:Min(uint,uint):uint We should keep an eye on benchmarks that may be impacted by these (cc @dotnet/jit-contrib), but overall it seems great to me to use conditional moves for these primitive methods. The diffs look like: ;; size=8 bbWeight=1 PerfScore 1.50
-G_M1277_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
+G_M1277_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
uxth w0, w0
uxth w1, w1
cmp w0, w1
- beq G_M1277_IG06
- ;; size=16 bbWeight=1 PerfScore 2.50
-G_M1277_IG03: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
+ csel w2, w0, w1, lt
cmp w0, w1
- blt G_M1277_IG05
- mov w0, w1
- ;; size=12 bbWeight=0.50 PerfScore 1.00
-G_M1277_IG04: ; , epilog, nogc, extend
+ csel w0, w1, w2, eq
+ ;; size=24 bbWeight=1 PerfScore 3.00
+G_M1277_IG03: ; , epilog, nogc, extend
ldp fp, lr, [sp], #0x10
ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
-G_M1277_IG05: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, epilog, nogc
- ldp fp, lr, [sp], #0x10
- ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
-G_M1277_IG06: ; gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref
- mov w0, w1
- ;; size=4 bbWeight=0.50 PerfScore 0.25
-G_M1277_IG07: ; , epilog, nogc, extend
- ldp fp, lr, [sp], #0x10
- ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
+ ;; size=8 bbWeight=1 PerfScore 2.00 The double compare looks like an instance of #78385.
-G_M56171_IG01: ; gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref, nogc <-- Prolog IG
+G_M56171_IG01: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, nogc <-- Prolog IG
stp fp, lr, [sp, #-0x10]!
mov fp, sp
;; size=8 bbWeight=1 PerfScore 1.50
-G_M56171_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
+G_M56171_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
cmp w0, w1
- bge G_M56171_IG05
- ;; size=8 bbWeight=1 PerfScore 1.50
-G_M56171_IG03: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
- mov w0, w1
- ;; size=4 bbWeight=0.50 PerfScore 0.25
-G_M56171_IG04: ; , epilog, nogc, extend
+ csel w0, w0, w1, ge
+ ;; size=8 bbWeight=1 PerfScore 1.00
+G_M56171_IG03: ; , epilog, nogc, extend
ldp fp, lr, [sp], #0x10
ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
-G_M56171_IG05: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, epilog, nogc
- ldp fp, lr, [sp], #0x10
- ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
+ ;; size=8 bbWeight=1 PerfScore 2.00 Another one: -G_M6195_IG01: ; gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref, nogc <-- Prolog IG
+G_M6195_IG01: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, nogc <-- Prolog IG
stp fp, lr, [sp, #-0x10]!
mov fp, sp
;; size=8 bbWeight=1 PerfScore 1.50
-G_M6195_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
+G_M6195_IG02: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
+ mov w1, #1
tst w0, #0xD1FFAB1E
- beq G_M6195_IG05
- ;; size=8 bbWeight=1 PerfScore 1.50
-G_M6195_IG03: ; gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
- mov w0, wzr
- ;; size=4 bbWeight=0.50 PerfScore 0.25
-G_M6195_IG04: ; , epilog, nogc, extend
+ csel w0, w1, wzr, eq
+ ;; size=12 bbWeight=1 PerfScore 1.50
+G_M6195_IG03: ; , epilog, nogc, extend
ldp fp, lr, [sp], #0x10
ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
-G_M6195_IG05: ; gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref
- mov w0, #1
- ;; size=4 bbWeight=0.50 PerfScore 0.25
-G_M6195_IG06: ; , epilog, nogc, extend
- ldp fp, lr, [sp], #0x10
- ret lr
- ;; size=8 bbWeight=0.50 PerfScore 1.00
+ ;; size=8 bbWeight=1 PerfScore 2.00 Given the heuristics on if-conversion around loops there's probably not much to worry about here, and overall the diffs look great. |
Thanks again! |
For example:
if (x < 7) { a = 5; } else { a = 9; }
a = (cond) ? b : c;
The else condition must write to the same variable as the then statement.
This patch builds on top of #73472
It refactors a lot of the code in the If Conversion pass. Various parts are split into new functions. Following the style of other passes, I put everything inside it's own OptIfConversionDsc class too.
Detection is slightly reworked - It now finds a valid flow and then checks all the statements inside that flow are valid. I think this is slightly better for performance.
The existing if conversion logic should be unchanged - all the checks and modifications are the same.
This does not catch GT_RETURN cases, for example:
return (a==b) ? 1 : 0
It should be possible to add that on top of this.