You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Double constants usage in a loop can be CSEed #35257
Doubles present in a loop are reloaded repeatedly. Instead they can be set just once out of loop and use it inside the loop.
privatestaticdoubleProcess(doublen){doubleres;res=1;while(n>0.0){res*=n;// User might not write such code, but here it is written merely to show that same constants are re-loaded in assembly code.n-=1.0;n-=2.0;n-=1.0;n-=2.0;}returnres;}
As I discussed offline with @kunalspathak , doing this without unduly pessimizing cases with high register pressure may also require rematerialization (#6264)
(one needs to set TC_QuickJitForLoops=1 to use the static readonlies as "consts").
; ...xoredi,edimovrax,7AE147AE147Bvmovqxmm7,raxM00_L00: vmovaps xmm0,xmm6call System.Math.Sin(Double) vmovsd qword ptr [rsi+rdi*8],xmm0incrdi vmovaps xmm0,xmm7 ; not needed vaddsd xmm6,xmm6,xmm0cmprdi,3E8jb short M00_L00; ...
(note 1: at the comment vaddsd xmm6,xmm6,xmm7 would be ideal)
(note 2: instead of xmm6 and xmm7 could other registers be used too? So that they must not be saved according call-convention?)
* BitConverter uses SSE2 as workaround
** for pre-computing as it's outside of a critical section it won't matter, but for computing
We do hoist these now for x86/x64. We don't for Arm64 because these are viewed as "cheap" to materialize given they are small constants that can be embedded as an immediate.
They are given a gtCost of 1, which puts them below the MIN_CSE_COST threshold (currently 2).
We could play with increasing the gtCost such that all constants (at least floating-point ones) can be CSE'd, but that will likely also require some special handling in constant prop to ensure that special codegen opportunities are still accounted for (most notably for cases that are known to allow better instruction sequences to be emitted, such as if the constant can be contained as 0).
We could also play with allowing a something like MIN_CSE_COST_IN_LOOP, so that in a loop anything can be CSE'd, or special casing CSE of constants in a loop.
I think either would give a good balance between ensuring loop code stays efficient and ensuring that we don't accidentally pessimize codegen for cases where hoisting a constant prevents us from optimizing.
Personally, I think the idea of allowing anything to be CSE'd is the better option. We should have already done forward sub, morph, value numbering, and CSE by the time we get to proper constant prop. So, we should have already a decent view of things.
One might want to have value numbering or CSE account for cases where we would try to undo a CSE, to ensure costing remains correctly tracked, but that is likely a more complex change than a limited amount of "undo" for special constants like zero. So I think we could do a targeted fix here to improve things and work towards improving it more over time.
Activity
kunalspathak commentedon Apr 21, 2020
@CarolEidt @BruceForstall
CarolEidt commentedon Apr 21, 2020
As I discussed offline with @kunalspathak , doing this without unduly pessimizing cases with high register pressure may also require rematerialization (#6264)
EgorBo commentedon Apr 21, 2020
I guess this one is not arm specific as I see exactly the same picture on x86
(constants are not saved to registers before the loop)
kunalspathak commentedon Apr 21, 2020
Agree. I will update the title/label.
[-]ARM64 : Double constants usage in a loop can be CSEed[/-][+]Double constants usage in a loop can be CSEed[/+]tannergooding commentedon Apr 21, 2020
For reference on the x64 side: https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABAJgEYBYAKGIGYACMhgYQYG8aHuGAHKAJYA3bBhhNySBgBMIAV2AAbcQAUoEMDFy4AFLIXKGAOwCUNTtW76l42LgDcXBnYYBeBuUeWGAdwAWAoY6RgwAfAwADAB0EWbeFjzOWgwAVO5GXgD0mQwAqrgwUAz4AgDmfhjGEJU+gmIMuHJgfgyQ0jBoDMBylX6F4gKVAri+dWIh+P2KAJ4MGBANfhA+c36iDdiTrRBGuBjYRhgj2LBJcIoQ2O3SDAIheAX4SrNtMFFO3CFw7uQxXolfdykP4fYwMb4eEHeT7goFQ7gAXycxAA7EkHDQkdQEUA===
EgorBo commentedon Apr 21, 2020
Ah, by "hoisted out of loop" I meant to save
1and2to some xmm registers before the loop.Not sure it makes sense here tho.
gcc saves them: https://godbolt.org/z/gToudN
BruceForstall commentedon Apr 22, 2020
fyi, for x64 it looks like we're creating multiple constant pool entries for identical values, so I added #35268 to address that.
1 remaining item
gfoidl commentedon Nov 13, 2020
(At least on x64) this can be worked around by BitConverter-tricks*
Does this work by breaking constant propagation and CSE kicks in properly?
A more realistic repro is (pre-)computing** some heavy values to a table / computing results in batches like
It makes no difference if the increment
const double inc = 0.01;is defined outside the loop explicitely or not.The workaround here is
(one needs to set
TC_QuickJitForLoops=1to use the static readonlies as "consts").(note 1: at the comment
vaddsd xmm6,xmm6,xmm7would be ideal)(note 2: instead of
xmm6andxmm7could other registers be used too? So that they must not be saved according call-convention?)* BitConverter uses SSE2 as workaround
** for pre-computing as it's outside of a critical section it won't matter, but for computing
EgorBo commentedon Nov 13, 2020
@gfoidl I think(hope) it will be fixed via #44419
tannergooding commentedon Feb 5, 2024
We do hoist these now for x86/x64. We don't for Arm64 because these are viewed as "cheap" to materialize given they are small constants that can be embedded as an immediate.
They are given a
gtCostof1, which puts them below the MIN_CSE_COST threshold (currently 2).We could play with increasing the
gtCostsuch that all constants (at least floating-point ones) can be CSE'd, but that will likely also require some special handling in constant prop to ensure that special codegen opportunities are still accounted for (most notably for cases that are known to allow better instruction sequences to be emitted, such as if the constant can be contained as0).We could also play with allowing a something like
MIN_CSE_COST_IN_LOOP, so that in a loop anything can be CSE'd, or special casing CSE of constants in a loop.I think either would give a good balance between ensuring loop code stays efficient and ensuring that we don't accidentally pessimize codegen for cases where hoisting a constant prevents us from optimizing.
tannergooding commentedon Feb 5, 2024
Personally, I think the idea of allowing anything to be CSE'd is the better option. We should have already done forward sub, morph, value numbering, and CSE by the time we get to proper constant prop. So, we should have already a decent view of things.
One might want to have value numbering or CSE account for cases where we would try to undo a CSE, to ensure costing remains correctly tracked, but that is likely a more complex change than a limited amount of "undo" for special constants like
zero. So I think we could do a targeted fix here to improve things and work towards improving it more over time.