[jit] Align float/double Nan/Inf to uint conversion behavior with CoreCLR/LLVM/Roslyn...
authorFilip Navara <navara@emclient.com>
Thu, 1 Aug 2019 17:37:41 +0000 (19:37 +0200)
committerSteve Pfister <steveisok@users.noreply.github.com>
Thu, 1 Aug 2019 17:37:41 +0000 (13:37 -0400)
* [jit] Align float/double Nan/Inf to uint conversion behavior with CoreCLR/LLVM

* Fix float/double -> ulong conversion for edge cases

* Fix mono_fconv_u8 / mono_rconv_u8 to also return 0 for PositiveInfinity

* Enable support for platform specific implementations of OP_RCONV_U8/OP_FCONV_U8/OP_FCONV_U4

* Restore OP emulation on WASM target

* Update tests

* Disable failing tests for now

* Adjust tests

* Adjust tests

* Fix float -> uint conversion on WASM

* Make interpreter consistent with compiled code

* Blind attempt to fix WASM test leg

* Implement remaining conversions in LLVM

* Fix ARM32

* Workaround for Windows x64 FullAOT+LLVM

* Bump AOT version

Commit migrated from https://github.com/mono/mono/commit/3c92cd8c94c027b7c9215a12044345106851262b

19 files changed:
src/mono/mono/metadata/icall-signatures.h
src/mono/mono/metadata/jit-icall-reg.h
src/mono/mono/mini/aot-runtime.h
src/mono/mono/mini/basic-float.cs
src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/jit-icalls.c
src/mono/mono/mini/jit-icalls.h
src/mono/mono/mini/mini-amd64.c
src/mono/mono/mini/mini-amd64.h
src/mono/mono/mini/mini-arm.h
src/mono/mono/mini/mini-llvm.c
src/mono/mono/mini/mini-mips.h
src/mono/mono/mini/mini-ppc.h
src/mono/mono/mini/mini-riscv.h
src/mono/mono/mini/mini-runtime.c
src/mono/mono/mini/mini-sparc.h
src/mono/mono/mini/mini-wasm.h
src/mono/mono/mini/mini-x86.h
src/mono/netcore/CoreFX.issues.rsp

index 6425e7e..aa15e21 100644 (file)
@@ -49,6 +49,7 @@
 // mono_icall_sig_ptr_ptr
 // mono_icall_sig_uint16_double
 // mono_icall_sig_uint32_double
+// mono_icall_sig_uint32_float
 // mono_icall_sig_uint8_double
 // mono_icall_sig_ulong_double
 // mono_icall_sig_ulong_float
@@ -173,6 +174,7 @@ ICALL_SIG (2, (ptr, object))                        \
 ICALL_SIG (2, (ptr, ptr))                      \
 ICALL_SIG (2, (uint16, double))                        \
 ICALL_SIG (2, (uint32, double))                        \
+ICALL_SIG (2, (uint32, float))                 \
 ICALL_SIG (2, (uint8, double))                 \
 ICALL_SIG (2, (ulong, double))                 \
 ICALL_SIG (2, (ulong, float))                  \
index c48724c..4bc81d1 100644 (file)
@@ -116,6 +116,7 @@ MONO_JIT_ICALL (__emul_rconv_to_i8) \
 MONO_JIT_ICALL (__emul_rconv_to_ovf_i8) \
 MONO_JIT_ICALL (__emul_rconv_to_ovf_u8) \
 MONO_JIT_ICALL (__emul_rconv_to_ovf_u8_un) \
+MONO_JIT_ICALL (__emul_rconv_to_u4)    \
 MONO_JIT_ICALL (__emul_rconv_to_u8) \
 MONO_JIT_ICALL (__emul_rrem) \
 MONO_JIT_ICALL (cominterop_get_ccw) \
index dd0bce5..0d07118 100644 (file)
@@ -11,7 +11,7 @@
 #include "mini.h"
 
 /* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION 168
+#define MONO_AOT_FILE_VERSION 169
 
 #define MONO_AOT_TRAMP_PAGE_SIZE 16384
 
index e6a1445..b4004ee 100644 (file)
@@ -80,27 +80,80 @@ class Tests
                sbyte sb = (sbyte)a;
                if (sb != 2)
                        return 6;
-               /* MS.NET special cases these */
+               return 0;
+       }
+
+       public static int test_0_fconv_u8 () {
                double d = Double.NaN;
-               ui = (uint)d;
+               ulong ui;
+               ui = (ulong)d;
                if (ui != 0)
-                       return 7;
+                       return 1;
                d = Double.PositiveInfinity;
+               ui = (ulong)d;
+               /* x64: 0, ARM64: ulong.MaxValue (verified against CoreCLR) */
+               if (ui != 0 && ui != ulong.MaxValue)
+                       return 2;
+               d = Double.NegativeInfinity;
+               ui = (ulong)d;
+               /* x64: long.MaxValue + 1 (inconsistent with conversion to u4 but matches CLR) */
+               if (ui != 0 && ui != (ulong)long.MaxValue + 1)
+                       return 3;
+               return 0;
+       }
+
+       public static int test_0_fconv_u4 () {
+               double d = Double.NaN;
+               uint ui;
                ui = (uint)d;
                if (ui != 0)
-                       return 8;
+                       return 1;
+               d = Double.PositiveInfinity;
+               ui = (uint)d;
+               /* x64: 0, ARM64: uint.MaxValue (verified against CoreCLR) */
+               if (ui != 0 && ui != uint.MaxValue)
+                       return 2;
                d = Double.NegativeInfinity;
                ui = (uint)d;
                if (ui != 0)
-                       return 9;
-               /* FIXME: This fails with llvm and with gcc -O2 on osx/linux */
-               /*
-               d = Double.MaxValue;
-               i = (int)d;
-               if (i != -2147483648)
-                       return 10;
-               */
+                       return 3;
+               return 0;
+       }
+
+       public static int test_0_rconv_u8 () {
+               float d = float.NaN;
+               ulong ui;
+               ui = (ulong)d;
+               if (ui != 0)
+                       return 1;
+               d = float.PositiveInfinity;
+               ui = (ulong)d;
+               /* x64: 0, ARM64: ulong.MaxValue (verified against CoreCLR) */
+               if (ui != 0 && ui != ulong.MaxValue)
+                       return 2;
+               d = float.NegativeInfinity;
+               ui = (ulong)d;
+               /* x64: long.MaxValue + 1 (inconsistent with conversion to u4 but matches CLR) */
+               if (ui != 0 && ui != (ulong)long.MaxValue + 1)
+                       return 3;
+               return 0;
+       }
 
+       public static int test_0_rconv_u4 () {
+               float d = float.NaN;
+               uint ui;
+               ui = (uint)d;
+               if (ui != 0)
+                       return 1;
+               d = float.PositiveInfinity;
+               ui = (uint)d;
+               /* x64: 0, ARM64: uint.MaxValue (verified against CoreCLR) */
+               if (ui != 0 && ui != uint.MaxValue)
+                       return 2;
+               d = float.NegativeInfinity;
+               ui = (uint)d;
+               if (ui != 0)
+                       return 3;
                return 0;
        }
 
index 02a728b..a9365f5 100644 (file)
@@ -4245,25 +4245,19 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CONV_U4_R4)
-                       /* needed on arm64 */
-                       if (isinf (sp [-1].data.f_r4))
-                               sp [-1].data.i = 0;
-                       /* needed by wasm */
-                       else if (isnan (sp [-1].data.f_r4))
-                               sp [-1].data.i = 0;
-                       else
-                               sp [-1].data.i = (guint32) sp [-1].data.f_r4;
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U4
+                       sp [-1].data.i = mono_rconv_u4 (sp [-1].data.f_r4);
+#else
+                       sp [-1].data.i = (guint32) sp [-1].data.f_r4;
+#endif
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CONV_U4_R8)
-                       /* needed on arm64 */
-                       if (mono_isinf (sp [-1].data.f))
-                               sp [-1].data.i = 0;
-                       /* needed by wasm */
-                       else if (isnan (sp [-1].data.f))
-                               sp [-1].data.i = 0;
-                       else
-                               sp [-1].data.i = (guint32)sp [-1].data.f;
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U4
+                       sp [-1].data.i = mono_fconv_u4_2 (sp [-1].data.f);
+#else
+                       sp [-1].data.i = (guint32) sp [-1].data.f;
+#endif
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CONV_I8_I4)
@@ -4319,11 +4313,19 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CONV_U8_R4)
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U8
+                       sp [-1].data.l = mono_rconv_u8 (sp [-1].data.f_r4);
+#else
                        sp [-1].data.l = (guint64) sp [-1].data.f_r4;
+#endif
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CONV_U8_R8)
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U8
+                       sp [-1].data.l = mono_fconv_u8_2 (sp [-1].data.f);
+#else
                        sp [-1].data.l = (guint64)sp [-1].data.f;
+#endif
                        ++ip;
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_CPOBJ) {
index 108a8fe..17a3c29 100644 (file)
@@ -852,9 +852,21 @@ mono_ldtoken_wrapper_generic_shared (MonoImage *image, int token, MonoMethod *me
 guint64
 mono_fconv_u8 (double v)
 {
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
+       const double two63 = 2147483648.0 * 4294967296.0;
+       if (v < two63) {
+               return (gint64)v;
+       } else {
+               return (gint64)(v - two63) + ((guint64)1 << 63);
+       }
+#else
+       if (mono_isinf (v) || mono_isnan (v))
+               return 0;
        return (guint64)v;
+#endif
 }
 
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U8
 guint64
 mono_fconv_u8_2 (double v)
 {
@@ -869,8 +881,20 @@ mono_fconv_u8_2 (double v)
 guint64
 mono_rconv_u8 (float v)
 {
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
+       const float two63 = 2147483648.0 * 4294967296.0;
+       if (v < two63) {
+               return (gint64)v;
+       } else {
+               return (gint64)(v - two63) + ((guint64)1 << 63);
+       }
+#else
+       if (mono_isinf (v) || mono_isnan (v))
+               return 0;
        return (guint64)v;
+#endif
 }
+#endif
 
 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
 gint64
@@ -889,6 +913,7 @@ mono_fconv_u4 (double v)
        return (guint32)v;
 }
 
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U4
 guint32
 mono_fconv_u4_2 (double v)
 {
@@ -900,6 +925,15 @@ mono_fconv_u4_2 (double v)
        return mono_fconv_u4 (v);
 }
 
+gint32
+mono_rconv_u4 (float v)
+{
+       if (mono_isinf (v) || mono_isnan (v))
+               return 0;
+       return (gint32)v;
+}
+#endif
+
 gint64
 mono_fconv_ovf_i8 (double v)
 {
index bb0bdc0..66548d2 100644 (file)
@@ -80,6 +80,8 @@ G_EXTERN_C gint64 mono_fconv_i8 (double v);
 G_EXTERN_C guint32 mono_fconv_u4 (double v);
 G_EXTERN_C guint32 mono_fconv_u4_2 (double v);
 
+G_EXTERN_C gint32 mono_rconv_u4 (float v);
+
 G_EXTERN_C gint64 mono_fconv_ovf_i8 (double v);
 
 G_EXTERN_C guint64 mono_fconv_ovf_u8 (double v);
index 8a8018e..ed95c38 100644 (file)
@@ -3491,7 +3491,8 @@ cc_signed_table [] = {
 static unsigned char*
 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
 {
-       if (size == 8)
+       // Use 8 as register size to get Nan/Inf conversion to uint result truncated to 0
+       if (size == 8 || (!is_signed && size == 4))
                amd64_sse_cvttsd2si_reg_reg (code, dreg, sreg);
        else
                amd64_sse_cvttsd2si_reg_reg_size (code, dreg, sreg, 4);
@@ -5403,7 +5404,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        amd64_sse_cvtss2si_reg_reg_size (code, ins->dreg, ins->sreg1, 4);
                        break;
                case OP_RCONV_TO_U4:
-                       amd64_sse_cvtss2si_reg_reg_size (code, ins->dreg, ins->sreg1, 4);
+                       // Use 8 as register size to get Nan/Inf conversion result truncated to 0
+                       amd64_sse_cvtss2si_reg_reg (code, ins->dreg, ins->sreg1);
                        break;
                case OP_RCONV_TO_I8:
                case OP_RCONV_TO_I:
index d020c16..8c3db26 100644 (file)
@@ -411,7 +411,12 @@ typedef struct {
 #define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
 #define MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS
 
-#define MONO_ARCH_EMULATE_CONV_R8_UN    1
+#define MONO_ARCH_EMULATE_CONV_R8_UN 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
+// The Windows x64 FullAOT+LLVM fails to pass the basic-float tests without this.
+#ifdef TARGET_WIN32
+#define MONO_ARCH_EMULATE_FCONV_TO_U4 1
+#endif
 #define MONO_ARCH_EMULATE_FREM 1
 #define MONO_ARCH_HAVE_IS_INT_OVERFLOW 1
 #define MONO_ARCH_HAVE_INVALIDATE_METHOD 1
index 757c00d..856c642 100644 (file)
@@ -310,6 +310,7 @@ typedef struct MonoCompileArch {
 } MonoCompileArch;
 
 #define MONO_ARCH_EMULATE_FCONV_TO_I8 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
 #define MONO_ARCH_EMULATE_LCONV_TO_R8 1
 #define MONO_ARCH_EMULATE_LCONV_TO_R4 1
 #define MONO_ARCH_EMULATE_LCONV_TO_R8_UN 1
index f91a246..9b8f4e2 100644 (file)
@@ -777,8 +777,12 @@ op_to_llvm_type (int opcode)
        case OP_RCONV_TO_I2:
        case OP_RCONV_TO_U2:
                return LLVMInt16Type ();
+       case OP_FCONV_TO_U4:
        case OP_RCONV_TO_U4:
                return LLVMInt32Type ();
+       case OP_FCONV_TO_U8:
+       case OP_RCONV_TO_U8:
+               return LLVMInt64Type ();
        case OP_FCONV_TO_I:
        case OP_FCONV_TO_U:
                return TARGET_SIZEOF_VOID_P == 8 ? LLVMInt64Type () : LLVMInt32Type ();
@@ -5485,9 +5489,14 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                case OP_RCONV_TO_U2:
                        values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
                        break;
+               case OP_FCONV_TO_U4:
                case OP_RCONV_TO_U4:
                        values [ins->dreg] = LLVMBuildFPToUI (builder, lhs, LLVMInt32Type (), dname);
                        break;
+               case OP_FCONV_TO_U8:
+               case OP_RCONV_TO_U8:
+                       values [ins->dreg] = LLVMBuildFPToUI (builder, lhs, LLVMInt64Type (), dname);
+                       break;
                case OP_FCONV_TO_I8:
                case OP_RCONV_TO_I8:
                        values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt64Type (), dname);
index 4071b73..e3fabff 100644 (file)
@@ -236,6 +236,8 @@ typedef struct MonoCompileArch {
 #define MONO_ARCH_EMULATE_FREM 1
 #endif
 
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
+
 /*
  * mips backend misses some instructions that enable emitting of optimal
  * code on other targets and, additionally, the register allocator gets
index 1f84b29..243286d 100644 (file)
@@ -103,6 +103,7 @@ typedef struct MonoCompileArch {
 #define MONO_ARCH_EMULATE_LCONV_TO_R4 1
 #endif
 
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
 #define MONO_ARCH_EMULATE_LCONV_TO_R8_UN 1
 #define MONO_ARCH_EMULATE_FREM 1
 #define MONO_ARCH_GC_MAPS_SUPPORTED 1
index f45ea90..17c7045 100644 (file)
 #endif
 
 #define MONO_ARCH_EMULATE_CONV_R8_UN        (1)
+#define MONO_ARCH_EMULATE_FCONV_TO_U8       (1)
+#define MONO_ARCH_EMULATE_FCONV_TO_U4       (1)
 #define MONO_ARCH_EMULATE_FCONV_TO_I8       (1)
 #define MONO_ARCH_EMULATE_LCONV_TO_R8       (1)
 #define MONO_ARCH_EMULATE_LCONV_TO_R4       (1)
index 9ad2893..b5de211 100644 (file)
@@ -4468,9 +4468,14 @@ register_icalls (void)
        register_opcode_emulation (OP_FDIV, __emul_fdiv, mono_icall_sig_double_double_double, mono_fdiv, FALSE);
 #endif
 
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U8
        register_opcode_emulation (OP_FCONV_TO_U8, __emul_fconv_to_u8, mono_icall_sig_ulong_double, mono_fconv_u8_2, FALSE);
        register_opcode_emulation (OP_RCONV_TO_U8, __emul_rconv_to_u8, mono_icall_sig_ulong_float, mono_rconv_u8, FALSE);
+#endif
+#ifdef MONO_ARCH_EMULATE_FCONV_TO_U4
        register_opcode_emulation (OP_FCONV_TO_U4, __emul_fconv_to_u4, mono_icall_sig_uint32_double, mono_fconv_u4_2, FALSE);
+       register_opcode_emulation (OP_RCONV_TO_U4, __emul_rconv_to_u4, mono_icall_sig_uint32_float, mono_rconv_u4, FALSE);
+#endif
        register_opcode_emulation (OP_FCONV_TO_OVF_I8, __emul_fconv_to_ovf_i8, mono_icall_sig_long_double, mono_fconv_ovf_i8, FALSE);
        register_opcode_emulation (OP_FCONV_TO_OVF_U8, __emul_fconv_to_ovf_u8, mono_icall_sig_ulong_double, mono_fconv_ovf_u8, FALSE);
        register_opcode_emulation (OP_FCONV_TO_OVF_U8_UN, __emul_fconv_to_ovf_u8_un, mono_icall_sig_ulong_double, mono_fconv_ovf_u8_un, FALSE);
index f6afabe..63be332 100644 (file)
@@ -95,6 +95,7 @@ typedef struct MonoCompileArch {
 /*#define MONO_ARCH_SIGSEGV_ON_ALTSTACK*/
 #endif
 
+#define MONO_ARCH_EMULATE_FCONV_TO_U8   1
 #define MONO_ARCH_EMULATE_FCONV_TO_I8   1
 #define MONO_ARCH_EMULATE_LCONV_TO_R8   1
 #define MONO_ARCH_EMULATE_LCONV_TO_R4   1
index bb4594e..06f1a5b 100644 (file)
@@ -25,6 +25,8 @@
 #define MONO_ARCH_NEED_DIV_CHECK 1
 
 #define MONO_ARCH_EMULATE_FREM 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U4 1
 #define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS 1
 #define MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS 1
 #define MONO_ARCH_FLOAT32_SUPPORTED 1
index 3d561df..af976b0 100644 (file)
@@ -189,6 +189,9 @@ typedef struct {
 /* Enables OP_LSHL, OP_LSHL_IMM, OP_LSHR, OP_LSHR_IMM, OP_LSHR_UN, OP_LSHR_UN_IMM */
 #define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
 
+#define MONO_ARCH_EMULATE_FCONV_TO_U8 1
+#define MONO_ARCH_EMULATE_FCONV_TO_U4 1
+
 #define MONO_ARCH_NEED_DIV_CHECK 1
 #define MONO_ARCH_HAVE_IS_INT_OVERFLOW 1
 #define MONO_ARCH_HAVE_INVALIDATE_METHOD 1
index d99d988..efb194b 100644 (file)
 -nomethod System.Linq.Expressions.Tests.ArrayIndexTests.NonZeroBasedOneDimensionalArrayIndex
 -nomethod System.Linq.Expressions.Tests.ArrayIndexTests.NonZeroBasedOneDimensionalArrayIndexMethod
 
-# Expected Float to uint to be 2147483648 but got 0... https://github.com/mono/mono/issues/14931
--nomethod System.Linq.Expressions.Tests.ConvertTests.ConvertFloatToUIntTest
--nomethod System.Linq.Expressions.Tests.ConvertTests.ConvertNullableFloatToUIntTest
--nomethod System.Linq.Expressions.Tests.ConvertTests.ConvertNullableFloatToNullableUIntTest
--nomethod System.Linq.Expressions.Tests.ConvertTests.ConvertFloatToNullableUIntTest
--noclass System.Linq.Expressions.Tests.LambdaTests
--noclass System.Linq.Expressions.Tests.LambdaDivideNullableTests
--nomethod System.Linq.Expressions.Tests.BinaryCoalesceTests.VerifyIL_NullableIntCoalesceToNullableInt
-
 # OOM Exception.  Weird
 # https://github.com/mono/mono/issues/14933
 -nomethod System.Linq.Expressions.Tests.ArrayBoundsTests.SingleNegativeBoundErrorMessage