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
Inefficient codegen for casts between same size types. #11413
As per dotnet/coreclr#20788 (comment), using BitConverter.SingleToInt32Bits, BitConverter.Int32BitsToSingle, BitConverter.DoubleToInt64Bits, and BitConverter.Int64BitsToDouble generates some "inefficient" code.
Currently BitConverter.SingleToInt32Bits is generating:
#33476 introduced a fix which works around the issue, but we are leaving this issue open to track the JIT getting a more general codegen improvement that isn't specific to BitConverter.
changed the title [-]Inefficient codegen for `BitConverter.SingleToInt32Bits` and `BitConverter.DoubleToInt64Bits`[/-][+]Inefficient codegen for casts between same size types.[/+]on May 27, 2020
I guess this is partially related to aforementioned issue - is there any reason float.isFinite() is safe while the rest of similar extensions using similar tools are unsafe (like IsInfinity)?
@Kein Looks like we just forgot to remove the unsafe modifier from the Single.IsInfinity method. It doesn't affect callers; they're not required to be unsafe. But it does provide some further evidence that we should consider stripping the unsafe modifier from types and functions that no longer need it, just as an overall code hygiene cleanup.
But it does provide some further evidence that we should consider stripping the unsafe modifier from types and functions that no longer need it, just as an overall code hygiene cleanup.
This seems like one of the simpler analyzers we could write, is there an issue suggesting it yet?
There's some limited discussion around it as part of #31354. Basically: if we have an analyzer that says "you're not using pointers, remove unsafe" alongside a language feature that enforces "this API doesn't use pointers but you need to wrap call sites within an unsafe block", we need to figure out how these two things interact together.
This got resolved some time back (#71567, as well as other commits by other JIT team members)
The movd for double <-> long is actually just a disassembly bug/quirk. movd and movq are the same instruction, it's just that movq is the preferred disassembly when operating on 64-bit registers. Looks like we're not accounting for that when we do the disasm dumping
x86 is expected to be less efficient for DoubleToInt64Bits and the inverse here. There's no singular instruction available since the register size is 32-bits and so we have to do handle the lower and upper halves through memory.
There is notably a way to load a double directly to/from memory using a specialized memory only version of movq on 32-bit, but that's a non-trivial amount of work and it hasn't bubbled up in priority yet. That work is tracked instead by #11626
Activity
GrabYourPitchforks commentedon Jan 31, 2020
Related PR: #6864, which makes use of this API
tannergooding commentedon Mar 20, 2020
#33476 introduced a fix which works around the issue, but we are leaving this issue open to track the JIT getting a more general codegen improvement that isn't specific to
BitConverter.[-]Inefficient codegen for `BitConverter.SingleToInt32Bits` and `BitConverter.DoubleToInt64Bits`[/-][+]Inefficient codegen for casts between same size types.[/+]Kein commentedon Sep 24, 2020
I guess this is partially related to aforementioned issue - is there any reason float.isFinite() is safe while the rest of similar extensions using similar tools are
unsafe(like IsInfinity)?GrabYourPitchforks commentedon Sep 24, 2020
@Kein Looks like we just forgot to remove the unsafe modifier from the
Single.IsInfinitymethod. It doesn't affect callers; they're not required to be unsafe. But it does provide some further evidence that we should consider stripping the unsafe modifier from types and functions that no longer need it, just as an overall code hygiene cleanup.tannergooding commentedon Sep 24, 2020
This seems like one of the simpler analyzers we could write, is there an issue suggesting it yet?
GrabYourPitchforks commentedon Sep 24, 2020
There's some limited discussion around it as part of #31354. Basically: if we have an analyzer that says "you're not using pointers, remove unsafe" alongside a language feature that enforces "this API doesn't use pointers but you need to wrap call sites within an unsafe block", we need to figure out how these two things interact together.
deeprobin commentedon Aug 16, 2022
In .NET 7 it generates
https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABAJgEYBYAKGIGYACMhgYQYG8aHuGAHKAJYA3bBhgMBAOwwMAGgAoAZgBsIohooCUHLjz3EA7AwBCAjCwiShMKGKgA6AMpSA5spgAVCAElpdUqYYuEqaANy63AC+NJFAA=
JulieLeeMSFT commentedon Aug 20, 2024
With .NET 9 Preview 7,
BitConverter.SingleToInt32Bits(float)generatesBitConverter.Int32BitsToSingle(int)generatesBitConverter.DoubleToInt64Bits(double)generatesBitConverter.Int64BitsToDouble(long)generatesJulieLeeMSFT commentedon Aug 21, 2024
@tannergooding, please check the generated encoding with .NET 9 above (#11413 (comment)).
float <-> intis generating the expected code.double <-> longis generatingvmovd, notvmovq. Do we need further optimization?cc @dotnet/jit-contrib
tannergooding commentedon Aug 21, 2024
This got resolved some time back (#71567, as well as other commits by other JIT team members)
The
movdfordouble <-> longis actually just a disassembly bug/quirk.movdandmovqare the same instruction, it's just thatmovqis the preferred disassembly when operating on 64-bit registers. Looks like we're not accounting for that when we do the disasm dumpingJulieLeeMSFT commentedon Aug 21, 2024
Very good.
Also checked
SingleToInt32Bits(float)on x86JulieLeeMSFT commentedon Aug 21, 2024
on x86,
Int32BitsToSingle(int):floatDoubleToInt64Bits(double):longInt64BitsToDouble(long):doubletannergooding commentedon Aug 21, 2024
👍
x86 is expected to be less efficient for
DoubleToInt64Bitsand the inverse here. There's no singular instruction available since the register size is 32-bits and so we have to do handle the lower and upper halves through memory.There is notably a way to load a double directly to/from memory using a specialized memory only version of
movqon 32-bit, but that's a non-trivial amount of work and it hasn't bubbled up in priority yet. That work is tracked instead by #11626