-
Notifications
You must be signed in to change notification settings - Fork 5k
Mono problems with double to nuint conversion #64570
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
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
CC @nealef |
This would be a bug in the mono logic. Any
This is actually something I'm looking into resolving more broadly and is being tracked by #61885. I have a prototype that fixes this here: #61761, but Mono is having issues (likely due to similar issues listed above) and so its still a WIP.
|
That seems like the right fix to me. (leaving aside the longer-term project of giving a consistent semantics to unsigned conversion for .NET). We run the |
I've just posted the final version of this fix (including the OP_FCONV_TO_U removal) as #64618. This passes the test suite on s390x for me and fixes the regression. |
* Emit OP_FCONV_TO_U8/U4 instead of OP_FCONV_TO_U. * Remove OP_FCONV_TO_U implementations across all back ends. * Remove duplicated mono_fconv_u8/u4 icall wrappers. * Fixes #64570
* Emit OP_FCONV_TO_U8/U4 instead of OP_FCONV_TO_U. * Remove OP_FCONV_TO_U implementations across all back ends. * Remove duplicated mono_fconv_u8/u4 icall wrappers. * Fixes #64570
After #64234 was merged, I now see failures on s390x (which uses Mono by default):
The failing test case does the following:
The expected value is computed by directly converting a float to
nuint
. The actual value is computed by first converting the float to a double, and then converting the double tonuint
. The float-to-double conversion is harmless. The problem is that float tonuint
and double tonuint
use quite different code paths in Mono.The code in
type_from_op
inmini/method-to-ir.c
does this:So on s390x the float-to-nuint case uses
OP_RCONV_TO_U8
, which is handled fine in the s390x back end. However, the double-to-nuint case usesOP_FCONV_TO_U
, which never even reaches the back end, because for this opcode, Mono unconditionally registers an emulation icall, seeregister_icalls
inmini/mini-runtime.c
:All other emulation icalls seem to be guarded by some
#ifdef MONO_ARCH_...
guard, but not this one. This will be unconditionally emulated even if the back end would handleOP_FCONV_TO_U8
and/orOP_FCONV_TO_U4
.The problem now comes from the fact that the emulation function does not fully model the expected semantics:
Conversion of a negative floating-point number to an unsigned type is expected to return 0. But this C implementation does not guarantee this. The explicit Intel implementation definitely does not do this for negative numbers, and the cross-platform pure C version runs into undefined behavior ("If the value of the integral part cannot be represented by the integer type, the behavior is undefined.").
On s390x, the actual behavior you'll see varies between compilers. By default, the runtime build uses clang, and clang defaults to targeting a very old s390x processor, which didn't yet have float-to-unsigned conversion instructions. So clang will instead emit code that does exactly what that Intel special case open-codes.
This has the effect that a negative floating-point input is converted to a signed integer first, and the result then reinterpreted as unsigned. Therefore we get from
(double)4567.0
first(intptr_t)-4567
and then(uintptr_t)18446744073709547049
.The other conversion goes directly from float to nuint, which uses
OP_RCONV_TO_U8
. This is implemented in the s390x back end by the single instruction that correctly handles all corner cases, so negative inputs convert to 0. This gives the mismatch.I think on Intel you don't see this problem because the mini-amd64.c back end does not implement
OP_RCONV_TO_U8
, and therefore this conversion also falls back to an emulated icall, which gives the same (wrong) result, so the test succeeds.Now I'm wondering what the right fix is. Simply changing
type_from_op
to this:fixes the failure for me. However, I'm not sure if this would affect other back ends. Also, this seems to make
OP_FCONV_TO_U
completely unused so there probably should be a bigger cleanup.Any thoughts or suggestions?
CC @tannergooding @vargaz @lambdageek
The text was updated successfully, but these errors were encountered: