diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index b9ebaa08546014..f9ea17a9079b49 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -397,8 +397,8 @@ enum CorInfoHelpFunc CORINFO_HELP_DBL2ULNG_OVF, CORINFO_HELP_FLTREM, CORINFO_HELP_DBLREM, - CORINFO_HELP_FLTROUND, - CORINFO_HELP_DBLROUND, + CORINFO_HELP_FLTROUND, // unused + CORINFO_HELP_DBLROUND, // unused /* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide which is the right helper to use to allocate an object of a given type. */ diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index a0982f3ac6520f..f6c2d532e78ce7 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -35,8 +35,6 @@ JITHELPER(CORINFO_HELP_UDIV, JIT_UDiv, CORINFO_HELP_SIG_8_STACK) JITHELPER(CORINFO_HELP_UMOD, JIT_UMod, CORINFO_HELP_SIG_8_STACK) - // CORINFO_HELP_DBL2INT, CORINFO_HELP_DBL2UINT, and CORINFO_HELP_DBL2LONG get - // patched for CPUs that support SSE2 (P4 and above). #ifndef TARGET_64BIT JITHELPER(CORINFO_HELP_LLSH, JIT_LLsh, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_LRSH, JIT_LRsh, CORINFO_HELP_SIG_REG_ONLY) @@ -47,26 +45,29 @@ JITHELPER(CORINFO_HELP_LRSZ, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) #endif // TARGET_64BIT JITHELPER(CORINFO_HELP_LMUL, JIT_LMul, CORINFO_HELP_SIG_16_STACK) - JITHELPER(CORINFO_HELP_LMUL_OVF, JIT_LMulOvf, CORINFO_HELP_SIG_16_STACK) - JITHELPER(CORINFO_HELP_ULMUL_OVF, JIT_ULMulOvf, CORINFO_HELP_SIG_16_STACK) + DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK) + DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, CORINFO_HELP_SIG_16_STACK) JITHELPER(CORINFO_HELP_LDIV, JIT_LDiv, CORINFO_HELP_SIG_16_STACK) JITHELPER(CORINFO_HELP_LMOD, JIT_LMod, CORINFO_HELP_SIG_16_STACK) JITHELPER(CORINFO_HELP_ULDIV, JIT_ULDiv, CORINFO_HELP_SIG_16_STACK) JITHELPER(CORINFO_HELP_ULMOD, JIT_ULMod, CORINFO_HELP_SIG_16_STACK) - JITHELPER(CORINFO_HELP_LNG2DBL, JIT_Lng2Dbl, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_ULNG2DBL, JIT_ULng2Dbl, CORINFO_HELP_SIG_8_STACK) - DYNAMICJITHELPER(CORINFO_HELP_DBL2INT, JIT_Dbl2Lng, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBL2INT_OVF, JIT_Dbl2IntOvf, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_LNG2DBL, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_ULNG2DBL, NULL, CORINFO_HELP_SIG_8_STACK) + JITHELPER(CORINFO_HELP_DBL2INT, JIT_Dbl2Int, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBL2INT_OVF, NULL, CORINFO_HELP_SIG_8_STACK) + // CORINFO_HELP_DBL2LNG and CORINFO_HELP_DBL2UINT get patched for CPUs with SSE3 in jitinterfacex86.cpp DYNAMICJITHELPER(CORINFO_HELP_DBL2LNG, JIT_Dbl2Lng, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBL2LNG_OVF, JIT_Dbl2LngOvf, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBL2LNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK) + // this relies on ABI returning the lower half of long the same as an uint + // and the upper half in a caller saver register DYNAMICJITHELPER(CORINFO_HELP_DBL2UINT, JIT_Dbl2Lng, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBL2UINT_OVF, JIT_Dbl2UIntOvf, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBL2ULNG, JIT_Dbl2ULng, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBL2ULNG_OVF, JIT_Dbl2ULngOvf, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, CORINFO_HELP_SIG_16_STACK) - JITHELPER(CORINFO_HELP_FLTROUND, JIT_FloatRound, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBLROUND, JIT_DoubleRound, CORINFO_HELP_SIG_16_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBL2UINT_OVF, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBL2ULNG, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBL2ULNG_OVF, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_FLTREM, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBLREM, NULL, CORINFO_HELP_SIG_16_STACK) + DYNAMICJITHELPER(CORINFO_HELP_FLTROUND, NULL, CORINFO_HELP_SIG_8_STACK) + DYNAMICJITHELPER(CORINFO_HELP_DBLROUND, NULL, CORINFO_HELP_SIG_16_STACK) // Allocating a new object JITHELPER(CORINFO_HELP_NEWFAST, JIT_New, CORINFO_HELP_SIG_REG_ONLY) @@ -203,7 +204,7 @@ JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE, JIT_GetSharedNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR, JIT_GetSharedGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR, JIT_GetSharedNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedGCThreadStaticBaseDynamicClass, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedGCThreadStaticBaseDynamicClass, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedNonGCThreadStaticBaseDynamicClass, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, JIT_GetSharedGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, JIT_GetSharedNonGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ac90918815856d..9409d1fb4ce312 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -5973,6 +5973,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) switch (tree->AsIntrinsic()->gtIntrinsicName) { case NI_System_Math_Atan2: + case NI_System_Math_FMod: case NI_System_Math_Pow: // These math intrinsics are actually implemented by user calls. Increase the // Sethi 'complexity' by two to reflect the argument register requirement. diff --git a/src/coreclr/nativeaot/Runtime/MathHelpers.cpp b/src/coreclr/nativeaot/Runtime/MathHelpers.cpp index 9c4220ef5a7eed..f334351acf3b0d 100644 --- a/src/coreclr/nativeaot/Runtime/MathHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MathHelpers.cpp @@ -5,81 +5,13 @@ #include "CommonMacros.h" #include "rhassert.h" -// -// Floating point and 64-bit integer math helpers. -// - -FCIMPL1_D(uint64_t, RhpDbl2ULng, double val) -{ - const double two63 = 2147483648.0 * 4294967296.0; - uint64_t ret; - if (val < two63) - { - ret = (int64_t)(val); - } - else - { - // subtract 0x8000000000000000, do the convert then add it back again - ret = (int64_t)(val - two63) + I64(0x8000000000000000); - } - return ret; -} -FCIMPLEND - #undef min #undef max #include -FCIMPL2_FF(float, RhpFltRem, float dividend, float divisor) -{ - // - // From the ECMA standard: - // - // If [divisor] is zero or [dividend] is infinity - // the result is NaN. - // If [divisor] is infinity, - // the result is [dividend] (negated for -infinity***). - // - // ***"negated for -infinity" has been removed from the spec - // - - if (divisor==0 || !std::isfinite(dividend)) - { - return -nanf(""); - } - else if (!std::isfinite(divisor) && !std::isnan(divisor)) - { - return dividend; - } - // else... - return fmodf(dividend,divisor); -} -FCIMPLEND - -FCIMPL2_DD(double, RhpDblRem, double dividend, double divisor) -{ - // - // From the ECMA standard: - // - // If [divisor] is zero or [dividend] is infinity - // the result is NaN. - // If [divisor] is infinity, - // the result is [dividend] (negated for -infinity***). - // - // ***"negated for -infinity" has been removed from the spec - // - if (divisor==0 || !std::isfinite(dividend)) - { - return -nan(""); - } - else if (!std::isfinite(divisor) && !std::isnan(divisor)) - { - return dividend; - } - // else... - return(fmod(dividend,divisor)); -} -FCIMPLEND +// +// Floating point and 64-bit integer math helpers. +// #ifndef HOST_64BIT EXTERN_C int64_t QCALLTYPE RhpLDiv(int64_t i, int64_t j) @@ -106,12 +38,6 @@ EXTERN_C uint64_t QCALLTYPE RhpULMod(uint64_t i, uint64_t j) return i % j; } -FCIMPL1_D(int64_t, RhpDbl2Lng, double val) -{ - return (int64_t)val; -} -FCIMPLEND - FCIMPL1_D(int32_t, RhpDbl2Int, double val) { return (int32_t)val; @@ -124,15 +50,9 @@ FCIMPL1_D(uint32_t, RhpDbl2UInt, double val) } FCIMPLEND -FCIMPL1_L(double, RhpLng2Dbl, int64_t val) -{ - return (double)val; -} -FCIMPLEND - -FCIMPL1_L(double, RhpULng2Dbl, uint64_t val) +FCIMPL1_D(int64_t, RhpDbl2Lng, double val) { - return (double)val; + return (int64_t)val; } FCIMPLEND @@ -363,6 +283,14 @@ FCIMPL1_F(float, tanhf, float x) return std::tanhf(x); FCIMPLEND +FCIMPL2_DD(double, fmod, double x, double y) + return std::fmod(x, y); +FCIMPLEND + +FCIMPL2_FF(float, fmodf, float x, float y) + return std::fmodf(x, y); +FCIMPLEND + FCIMPL3_DDD(double, fma, double x, double y, double z) return std::fma(x, y, z); FCIMPLEND diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs index 85fef043acb0e1..83f8a4722ee37a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -6,377 +6,121 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Internal.Runtime; - namespace Internal.Runtime.CompilerHelpers { /// - /// Math helpers for generated code. The helpers marked with [RuntimeExport] and the type - /// itself need to be public because they constitute a public contract with the .NET Native toolchain. + /// Math helpers for generated code. The helpers here are referenced by the runtime. /// internal static partial class MathHelpers { #if !TARGET_64BIT - // - // 64-bit checked multiplication for 32-bit platforms - // - private const string RuntimeLibrary = "*"; - // Helper to multiply two 32-bit uints - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Mul32x32To64(uint a, uint b) - { - return a * (ulong)b; - } - - // Helper to get high 32-bit of 64-bit int - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Hi32Bits(long a) - { - return (uint)(a >> 32); - } - - // Helper to get high 32-bit of 64-bit int - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Hi32Bits(ulong a) - { - return (uint)(a >> 32); - } - - [RuntimeExport("LMulOvf")] - public static long LMulOvf(long i, long j) - { - long ret; - - // Remember the sign of the result - int sign = (int)(Hi32Bits(i) ^ Hi32Bits(j)); - - // Convert to unsigned multiplication - if (i < 0) i = -i; - if (j < 0) j = -j; - - // Get the upper 32 bits of the numbers - uint val1High = Hi32Bits(i); - uint val2High = Hi32Bits(j); - - ulong valMid; - - if (val1High == 0) - { - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (uint)i); - } - else - { - if (val2High != 0) - goto ThrowExcep; - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (uint)j); - } - - // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; - - ret = (long)(Mul32x32To64((uint)i, (uint)j) + (valMid << 32)); - - // check for overflow - if (Hi32Bits(ret) < (uint)valMid) - goto ThrowExcep; - - if (sign >= 0) - { - // have we spilled into the sign bit? - if (ret < 0) - goto ThrowExcep; - } - else - { - ret = -ret; - // have we spilled into the sign bit? - if (ret > 0) - goto ThrowExcep; - } - return ret; - - ThrowExcep: - return ThrowLngOvf(); - } - - [RuntimeExport("ULMulOvf")] - public static ulong ULMulOvf(ulong i, ulong j) - { - ulong ret; - - // Get the upper 32 bits of the numbers - uint val1High = Hi32Bits(i); - uint val2High = Hi32Bits(j); - - ulong valMid; - - if (val1High == 0) - { - if (val2High == 0) - return Mul32x32To64((uint)i, (uint)j); - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (uint)i); - } - else - { - if (val2High != 0) - goto ThrowExcep; - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (uint)j); - } - - // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; - - ret = Mul32x32To64((uint)i, (uint)j) + (valMid << 32); - - // check for overflow - if (Hi32Bits(ret) < (uint)valMid) - goto ThrowExcep; - return ret; - - ThrowExcep: - return ThrowULngOvf(); - } - [LibraryImport(RuntimeLibrary)] [SuppressGCTransition] - private static partial ulong RhpULMod(ulong i, ulong j); + private static partial ulong RhpULMod(ulong dividend, ulong divisor); - public static ulong ULMod(ulong i, ulong j) + public static ulong ULMod(ulong dividend, ulong divisor) { - if (j == 0) - return ThrowULngDivByZero(); - else - return RhpULMod(i, j); - } - - [LibraryImport(RuntimeLibrary)] - [SuppressGCTransition] - private static partial long RhpLMod(long i, long j); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - public static long LMod(long i, long j) - { - if (j == 0) - return ThrowLngDivByZero(); - else if (j == -1 && i == long.MinValue) - return ThrowLngOvf(); - else - return RhpLMod(i, j); + return RhpULMod(dividend, divisor); } [LibraryImport(RuntimeLibrary)] [SuppressGCTransition] - private static partial ulong RhpULDiv(ulong i, ulong j); + private static partial long RhpLMod(long dividend, long divisor); - public static ulong ULDiv(ulong i, ulong j) + public static long LMod(long dividend, long divisor) { - if (j == 0) - return ThrowULngDivByZero(); - else - return RhpULDiv(i, j); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); + if (divisor == -1 && dividend == long.MinValue) + ThrowHelper.ThrowOverflowException(); + + return RhpLMod(dividend, divisor); } [LibraryImport(RuntimeLibrary)] [SuppressGCTransition] - private static partial long RhpLDiv(long i, long j); - - public static long LDiv(long i, long j) - { - if (j == 0) - return ThrowLngDivByZero(); - else if (j == -1 && i == long.MinValue) - return ThrowLngOvf(); - else - return RhpLDiv(i, j); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static long ThrowLngDivByZero() - { - throw new DivideByZeroException(); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong ThrowULngDivByZero() - { - throw new DivideByZeroException(); - } -#endif // TARGET_64BIT - - [RuntimeExport("Dbl2IntOvf")] - public static int Dbl2IntOvf(double val) - { - const double two31 = 2147483648.0; - - // Note that this expression also works properly for val = NaN case - if (val > -two31 - 1 && val < two31) - return unchecked((int)val); - - return ThrowIntOvf(); - } - - [RuntimeExport("Dbl2UIntOvf")] - public static uint Dbl2UIntOvf(double val) - { - // Note that this expression also works properly for val = NaN case - if (val > -1.0 && val < 4294967296.0) - return unchecked((uint)val); - - return ThrowUIntOvf(); - } - - [RuntimeExport("Dbl2LngOvf")] - public static long Dbl2LngOvf(double val) - { - const double two63 = 2147483648.0 * 4294967296.0; - - // Note that this expression also works properly for val = NaN case - // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. - if (val > -two63 - 0x402 && val < two63) - return unchecked((long)val); - - return ThrowLngOvf(); - } + private static partial ulong RhpULDiv(ulong dividend, ulong divisor); - [RuntimeExport("Dbl2ULngOvf")] - public static ulong Dbl2ULngOvf(double val) + public static ulong ULDiv(ulong dividend, ulong divisor) { - const double two64 = 2.0 * 2147483648.0 * 4294967296.0; - - // Note that this expression also works properly for val = NaN case - if (val > -1.0 && val < two64) - return unchecked((ulong)val); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - return ThrowULngOvf(); + return RhpULDiv(dividend, divisor); } - [RuntimeExport("Flt2IntOvf")] - public static int Flt2IntOvf(float val) - { - const double two31 = 2147483648.0; - - // Note that this expression also works properly for val = NaN case - if (val > -two31 - 1 && val < two31) - return ((int)val); - - return ThrowIntOvf(); - } + [LibraryImport(RuntimeLibrary)] + [SuppressGCTransition] + private static partial long RhpLDiv(long dividend, long divisor); - [RuntimeExport("Flt2LngOvf")] - public static long Flt2LngOvf(float val) + public static long LDiv(long dividend, long divisor) { - const double two63 = 2147483648.0 * 4294967296.0; - - // Note that this expression also works properly for val = NaN case - // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. - if (val > -two63 - 0x402 && val < two63) - return ((long)val); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); + if (divisor == -1 && dividend == long.MinValue) + ThrowHelper.ThrowOverflowException(); - return ThrowIntOvf(); + return RhpLDiv(dividend, divisor); } #if TARGET_ARM [RuntimeImport(RuntimeLibrary, "RhpIDiv")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int RhpIDiv(int i, int j); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int RhpIDiv(int dividend, int divisor); - public static int IDiv(int i, int j) + public static int IDiv(int dividend, int divisor) { - if (j == 0) - return ThrowIntDivByZero(); - else if (j == -1 && i == int.MinValue) - return ThrowIntOvf(); - else - return RhpIDiv(i, j); - } - - [RuntimeImport(RuntimeLibrary, "RhpUDiv")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern uint RhpUDiv(uint i, uint j); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); + if (divisor == -1 && dividend == int.MinValue) + ThrowHelper.ThrowOverflowException(); - public static long UDiv(uint i, uint j) - { - if (j == 0) - return ThrowUIntDivByZero(); - else - return RhpUDiv(i, j); + return RhpIDiv(dividend, divisor); } - [RuntimeImport(RuntimeLibrary, "RhpIMod")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int RhpIMod(int i, int j); + [RuntimeImport(RuntimeLibrary, "RhpUDiv")] + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern uint RhpUDiv(uint dividend, uint divisor); - public static int IMod(int i, int j) + public static long UDiv(uint dividend, uint divisor) { - if (j == 0) - return ThrowIntDivByZero(); - else if (j == -1 && i == int.MinValue) - return ThrowIntOvf(); - else - return RhpIMod(i, j); - } - - [RuntimeImport(RuntimeLibrary, "RhpUMod")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern uint RhpUMod(uint i, uint j); + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - public static long UMod(uint i, uint j) - { - if (j == 0) - return ThrowUIntDivByZero(); - else - return RhpUMod(i, j); + return RhpUDiv(dividend, divisor); } -#endif // TARGET_ARM - - // - // Matching return types of throw helpers enables tailcalling them. It improves performance - // of the hot path because of it does not need to raise full stackframe. - // - [MethodImpl(MethodImplOptions.NoInlining)] - private static int ThrowIntOvf() - { - throw new OverflowException(); - } + [RuntimeImport(RuntimeLibrary, "RhpIMod")] + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int RhpIMod(int dividend, int divisor); - [MethodImpl(MethodImplOptions.NoInlining)] - private static uint ThrowUIntOvf() + public static int IMod(int dividend, int divisor) { - throw new OverflowException(); - } + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); + if (divisor == -1 && dividend == int.MinValue) + ThrowHelper.ThrowOverflowException(); - [MethodImpl(MethodImplOptions.NoInlining)] - private static long ThrowLngOvf() - { - throw new OverflowException(); + return RhpIMod(dividend, divisor); } - [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong ThrowULngOvf() - { - throw new OverflowException(); - } + [RuntimeImport(RuntimeLibrary, "RhpUMod")] + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern uint RhpUMod(uint dividend, uint divisor); -#if TARGET_ARM - [MethodImpl(MethodImplOptions.NoInlining)] - private static int ThrowIntDivByZero() + public static long UMod(uint dividend, uint divisor) { - throw new DivideByZeroException(); - } + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - [MethodImpl(MethodImplOptions.NoInlining)] - private static uint ThrowUIntDivByZero() - { - throw new DivideByZeroException(); + return RhpUMod(dividend, divisor); } #endif // TARGET_ARM +#endif // TARGET_64BIT } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index cf1d04ca666af1..edb21b3bc21cf9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -164,54 +164,67 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, methodDesc = context.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeFieldHandle"); break; - case ReadyToRunHelper.Lng2Dbl: - mangledName = "RhpLng2Dbl"; - break; - case ReadyToRunHelper.ULng2Dbl: - mangledName = "RhpULng2Dbl"; - break; - - case ReadyToRunHelper.Dbl2Lng: - mangledName = "RhpDbl2Lng"; - break; case ReadyToRunHelper.Dbl2ULng: - mangledName = "RhpDbl2ULng"; - break; - case ReadyToRunHelper.Dbl2Int: - mangledName = "RhpDbl2Int"; - break; - case ReadyToRunHelper.Dbl2UInt: - mangledName = "RhpDbl2UInt"; + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertToUInt64", null); break; case ReadyToRunHelper.Dbl2IntOvf: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2IntOvf"); + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertToInt32Checked", null); break; case ReadyToRunHelper.Dbl2UIntOvf: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2UIntOvf"); + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertToUInt32Checked", null); break; case ReadyToRunHelper.Dbl2LngOvf: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2LngOvf"); + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertToInt64Checked", null); break; case ReadyToRunHelper.Dbl2ULngOvf: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2ULngOvf"); + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertToUInt64Checked", null); + break; + + case ReadyToRunHelper.Lng2Dbl: + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertFromInt64", null); + break; + case ReadyToRunHelper.ULng2Dbl: + methodDesc = context.SystemModule.GetKnownType("System", "Double").GetKnownMethod("ConvertFromUInt64", null); + break; + + case ReadyToRunHelper.LMulOfv: + methodDesc = context.SystemModule.GetKnownType("System", "Int64").GetKnownMethod("MultiplyChecked", null); + break; + case ReadyToRunHelper.ULMulOvf: + methodDesc = context.SystemModule.GetKnownType("System", "UInt64").GetKnownMethod("MultiplyChecked", null); + break; + + case ReadyToRunHelper.DblRound: + DefType doubleType = context.GetWellKnownType(WellKnownType.Double); + methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("Round", + new MethodSignature(MethodSignatureFlags.Static, 0, doubleType, [doubleType])); + break; + case ReadyToRunHelper.FltRound: + DefType floatType = context.GetWellKnownType(WellKnownType.Single); + methodDesc = context.SystemModule.GetKnownType("System", "MathF").GetKnownMethod("Round", + new MethodSignature(MethodSignatureFlags.Static, 0, floatType, [floatType])); break; case ReadyToRunHelper.DblRem: - mangledName = "RhpDblRem"; + methodDesc = context.SystemModule.GetKnownType("System", "Math").GetKnownMethod("FMod", null); break; case ReadyToRunHelper.FltRem: - mangledName = "RhpFltRem"; + methodDesc = context.SystemModule.GetKnownType("System", "MathF").GetKnownMethod("FMod", null); break; - case ReadyToRunHelper.LMul: - mangledName = "RhpLMul"; + case ReadyToRunHelper.Dbl2Int: + mangledName = "RhpDbl2Int"; break; - case ReadyToRunHelper.LMulOfv: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMulOvf"); + case ReadyToRunHelper.Dbl2UInt: + mangledName = "RhpDbl2UInt"; break; - case ReadyToRunHelper.ULMulOvf: - methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMulOvf"); + case ReadyToRunHelper.Dbl2Lng: + mangledName = "RhpDbl2Lng"; + break; + + case ReadyToRunHelper.LMul: + mangledName = "RhpLMul"; break; case ReadyToRunHelper.Mod: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 7ae867370bdf40..aa971734b5b70d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -1224,7 +1224,7 @@ private void ImportBinaryOperation(ILOpcode opcode) break; case ILOpcode.mul_ovf: case ILOpcode.mul_ovf_un: - if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM) + if (_compilation.TypeSystemContext.Target.PointerSize == 4) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.LMulOfv), "_lmulovf"); _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ULMulOvf), "_ulmulovf"); @@ -1245,6 +1245,10 @@ private void ImportBinaryOperation(ILOpcode opcode) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ThrowDivZero), "_divbyzero"); } + if (opcode == ILOpcode.div) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); + } break; case ILOpcode.rem: case ILOpcode.rem_un: @@ -1259,7 +1263,62 @@ private void ImportBinaryOperation(ILOpcode opcode) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ThrowDivZero), "_divbyzero"); } + if (opcode == ILOpcode.rem) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.DblRem), "rem"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.FltRem), "rem"); + break; + } + } + + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) + { + switch (wellKnownType) + { + case WellKnownType.Byte: + case WellKnownType.UInt16: + case WellKnownType.SByte: + case WellKnownType.Int16: + case WellKnownType.Int32: + if (checkOverflow) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2IntOvf), "conv_i4_ovf"); + } + break; + case WellKnownType.Int64: + if (checkOverflow) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2LngOvf), "conv_i8_ovf"); + } + break; + case WellKnownType.UInt32: + if (checkOverflow) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2UIntOvf), "conv_u4_ovf"); + } + break; + case WellKnownType.UInt64: + if (checkOverflow) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2ULngOvf), "conv_u8_ovf"); + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Dbl2ULng), "conv_u8"); + } break; + case WellKnownType.Single: + case WellKnownType.Double: + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Lng2Dbl), "conv_r"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ULng2Dbl), "conv_r"); + break; + } + if (checkOverflow) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); } } @@ -1388,7 +1447,6 @@ private static void ImportStoreIndirect(int token) { } private static void ImportStoreIndirect(TypeDesc type) { } private static void ImportShiftOperation(ILOpcode opcode) { } private static void ImportCompareOperation(ILOpcode opcode) { } - private static void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { } private static void ImportUnaryOperation(ILOpcode opCode) { } private static void ImportCpOpj(int token) { } private static void ImportCkFinite() { } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c52c58954165a2..54b41edf3f36f3 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -259,11 +259,39 @@ DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) -DEFINE_CLASS(INT128, System, Int128) -DEFINE_CLASS(UINT128, System, UInt128) +#ifdef FOR_ILLINK +DEFINE_CLASS(DOUBLE, System, Double) +#endif +DEFINE_METHOD(DOUBLE, CONVERT_TO_UINT64, ConvertToUInt64, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_TO_INT32_CHECKED,ConvertToInt32Checked, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_TO_UINT32_CHECKED,ConvertToUInt32Checked, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_TO_INT64_CHECKED,ConvertToInt64Checked, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_TO_UINT64_CHECKED,ConvertToUInt64Checked, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_FROM_INT64, ConvertFromInt64, NoSig) +DEFINE_METHOD(DOUBLE, CONVERT_FROM_UINT64, ConvertFromUInt64, NoSig) -DEFINE_CLASS(DYNAMICMETHOD, ReflectionEmit, DynamicMethod) +#ifdef FOR_ILLINK +DEFINE_CLASS(INT64, System, Int64) +#endif +DEFINE_METHOD(INT64, MULTIPLY_CHECKED, MultiplyChecked, NoSig) + +#ifdef FOR_ILLINK +DEFINE_CLASS(UINT64, System, UInt64) +#endif +DEFINE_METHOD(UINT64, MULTIPLY_CHECKED, MultiplyChecked, NoSig) + +DEFINE_CLASS(INT128, System, Int128) +DEFINE_CLASS(UINT128, System, UInt128) +DEFINE_CLASS(MATH, System, Math) +DEFINE_METHOD(MATH, ROUND, Round, SM_Dbl_RetDbl) +DEFINE_METHOD(MATH, FMOD, FMod, NoSig) + +DEFINE_CLASS(MATHF, System, MathF) +DEFINE_METHOD(MATHF, ROUND, Round, SM_Flt_RetFlt) +DEFINE_METHOD(MATHF, FMOD, FMod, NoSig) + +DEFINE_CLASS(DYNAMICMETHOD, ReflectionEmit, DynamicMethod) DEFINE_CLASS(DYNAMICRESOLVER, ReflectionEmit, DynamicResolver) DEFINE_FIELD(DYNAMICRESOLVER, DYNAMIC_METHOD, m_method) diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp index 7a9538d8ea7dd9..e4e2457e1700ef 100644 --- a/src/coreclr/vm/ecall.cpp +++ b/src/coreclr/vm/ecall.cpp @@ -156,6 +156,58 @@ void ECall::PopulateManagedHelpers() pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__SPAN_HELPERS__MEMCOPY)); pDest = pMD->GetMultiCallableAddrOfCode(); SetJitHelperFunction(CORINFO_HELP_MEMCPY, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_TO_UINT64)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBL2ULNG, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_TO_INT32_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBL2INT_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_TO_UINT32_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBL2UINT_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_TO_INT64_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBL2LNG_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_TO_UINT64_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBL2ULNG_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_FROM_INT64)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_LNG2DBL, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__DOUBLE__CONVERT_FROM_UINT64)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_ULNG2DBL, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__FMOD)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBLREM, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATHF__FMOD)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_FLTREM, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__INT64__MULTIPLY_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_LMUL_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__UINT64__MULTIPLY_CHECKED)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_ULMUL_OVF, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATH__ROUND)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_DBLROUND, pDest); + + pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__MATHF__ROUND)); + pDest = pMD->GetMultiCallableAddrOfCode(); + SetJitHelperFunction(CORINFO_HELP_FLTROUND, pDest); } static CrstStatic gFCallLock; diff --git a/src/coreclr/vm/i386/jithelp.S b/src/coreclr/vm/i386/jithelp.S index c1da6f4dcb8014..8eeb25ae592d30 100644 --- a/src/coreclr/vm/i386/jithelp.S +++ b/src/coreclr/vm/i386/jithelp.S @@ -552,85 +552,30 @@ LOCAL_LABEL(LRszMORE32): LEAF_END JIT_LRsz, _TEXT // *********************************************************************/ -// JIT_Dbl2LngP4x87 +// JIT_Dbl2LngSse3 // // Purpose: -// converts a double to a long truncating toward zero (C semantics) +// Converts a double to a long truncating toward zero (C semantics) // -// uses stdcall calling conventions +// Uses stdcall calling convention // -// This code is faster on a P4 than the Dbl2Lng code above, but is -// slower on a PIII. Hence we choose this code when on a P4 or above. +// This code is faster than C++ even on modern CPUs despite using the FPU +// but it requires SSE3 // -LEAF_ENTRY JIT_Dbl2LngP4x87, _TEXT - // get some local space - sub esp, 8 - - #define arg1 [esp + 0x0C] - fld QWORD PTR arg1 // fetch arg - fnstcw WORD PTR arg1 // store FPCW - movzx eax, WORD PTR arg1 // zero extend - wide - or ah, 0x0C // turn on OE and DE flags - mov DWORD PTR [esp], eax // store new FPCW bits - fldcw WORD PTR [esp] // reload FPCW with new bits - fistp QWORD PTR [esp] // convert - - // reload FP result - mov eax, DWORD PTR [esp] - mov edx, DWORD PTR [esp + 4] - - // reload original FPCW value - fldcw WORD PTR arg1 - #undef arg1 - - // restore stack - add esp, 8 - - ret -LEAF_END JIT_Dbl2LngP4x87, _TEXT - -// *********************************************************************/ -// JIT_Dbl2LngSSE3 -// -// Purpose: -// converts a double to a long truncating toward zero (C semantics) -// -// uses stdcall calling conventions -// -// This code is faster than the above P4 x87 code for Intel processors -// equal or later than Core2 and Atom that have SSE3 support -// -LEAF_ENTRY JIT_Dbl2LngSSE3, _TEXT +LEAF_ENTRY JIT_Dbl2LngSse3, _TEXT // get some local space sub esp, 8 fld QWORD PTR [esp + 0x0C] // fetch arg - fisttp QWORD PTR [esp] // convert - mov eax, DWORD PTR [esp] // reload FP result + fisttp QWORD PTR [esp] // convert + mov eax, DWORD PTR [esp] // reload FP result mov edx, DWORD PTR [esp + 4] // restore stack add esp, 8 ret -LEAF_END JIT_Dbl2LngSSE3, _TEXT - -// *********************************************************************/ -// JIT_Dbl2IntSSE2 -// -// Purpose: -// converts a double to a long truncating toward zero (C semantics) -// -// uses stdcall calling conventions -// -// This code is even faster than the P4 x87 code for Dbl2LongP4x87, -// but only returns a 32 bit value (only good for int). -// -LEAF_ENTRY JIT_Dbl2IntSSE2, _TEXT - movsd xmm0, [esp + 4] - cvttsd2si eax, xmm0 - ret -LEAF_END JIT_Dbl2IntSSE2, _TEXT +LEAF_END JIT_Dbl2LngSse3, _TEXT // *********************************************************************/ // JIT_StackProbe diff --git a/src/coreclr/vm/i386/jithelp.asm b/src/coreclr/vm/i386/jithelp.asm index 0faf7cde0e0b26..c67b7d94c335d2 100644 --- a/src/coreclr/vm/i386/jithelp.asm +++ b/src/coreclr/vm/i386/jithelp.asm @@ -36,11 +36,7 @@ JIT_LLsh TEXTEQU <_JIT_LLsh@0> JIT_LRsh TEXTEQU <_JIT_LRsh@0> JIT_LRsz TEXTEQU <_JIT_LRsz@0> JIT_LMul TEXTEQU <@JIT_LMul@16> -JIT_Dbl2LngOvf TEXTEQU <@JIT_Dbl2LngOvf@8> -JIT_Dbl2Lng TEXTEQU <@JIT_Dbl2Lng@8> -JIT_Dbl2IntSSE2 TEXTEQU <@JIT_Dbl2IntSSE2@8> -JIT_Dbl2LngP4x87 TEXTEQU <@JIT_Dbl2LngP4x87@8> -JIT_Dbl2LngSSE3 TEXTEQU <@JIT_Dbl2LngSSE3@8> +JIT_Dbl2LngSse3 TEXTEQU <@JIT_Dbl2LngSse3@8> JIT_InternalThrowFromHelper TEXTEQU <@JIT_InternalThrowFromHelper@4> JIT_WriteBarrierReg_PreGrow TEXTEQU <_JIT_WriteBarrierReg_PreGrow@0> JIT_WriteBarrierReg_PostGrow TEXTEQU <_JIT_WriteBarrierReg_PostGrow@0> @@ -638,144 +634,20 @@ LMul_hard: JIT_LMul ENDP ;*********************************************************************/ -; JIT_Dbl2LngOvf - -;Purpose: -; converts a double to a long truncating toward zero (C semantics) -; with check for overflow -; -; uses stdcall calling conventions -; -PUBLIC JIT_Dbl2LngOvf -JIT_Dbl2LngOvf PROC - fnclex - fld qword ptr [esp+4] - push ecx - push ecx - fstp qword ptr [esp] - call JIT_Dbl2Lng - mov ecx,eax - fnstsw ax - test ax,01h - jnz Dbl2LngOvf_throw - mov eax,ecx - ret 8 - -Dbl2LngOvf_throw: - mov ECX, CORINFO_OverflowException_ASM - call JIT_InternalThrowFromHelper - ret 8 -JIT_Dbl2LngOvf ENDP - -;*********************************************************************/ -; JIT_Dbl2Lng - -;Purpose: -; converts a double to a long truncating toward zero (C semantics) -; -; uses stdcall calling conventions +; JIT_Dbl2LngSse3 ; -; note that changing the rounding mode is very expensive. This -; routine basiclly does the truncation semantics without changing -; the rounding mode, resulting in a win. -; -PUBLIC JIT_Dbl2Lng -JIT_Dbl2Lng PROC - fld qword ptr[ESP+4] ; fetch arg - lea ecx,[esp-8] - sub esp,16 ; allocate frame - and ecx,-8 ; align pointer on boundary of 8 - fld st(0) ; duplciate top of stack - fistp qword ptr[ecx] ; leave arg on stack, also save in temp - fild qword ptr[ecx] ; arg, round(arg) now on stack - mov edx,[ecx+4] ; high dword of integer - mov eax,[ecx] ; low dword of integer - test eax,eax - je integer_QNaN_or_zero - -arg_is_not_integer_QNaN: - fsubp st(1),st ; TOS=d-round(d), - ; { st(1)=st(1)-st & pop ST } - test edx,edx ; what's sign of integer - jns positive - ; number is negative - ; dead cycle - ; dead cycle - fstp dword ptr[ecx] ; result of subtraction - mov ecx,[ecx] ; dword of difference(single precision) - add esp,16 - xor ecx,80000000h - add ecx,7fffffffh ; if difference>0 then increment integer - adc eax,0 ; inc eax (add CARRY flag) - adc edx,0 ; propagate carry flag to upper bits - ret 8 - -positive: - fstp dword ptr[ecx] ;17-18 ; result of subtraction - mov ecx,[ecx] ; dword of difference (single precision) - add esp,16 - add ecx,7fffffffh ; if difference<0 then decrement integer - sbb eax,0 ; dec eax (subtract CARRY flag) - sbb edx,0 ; propagate carry flag to upper bits - ret 8 - -integer_QNaN_or_zero: - test edx,7fffffffh - jnz arg_is_not_integer_QNaN - fstp st(0) ;; pop round(arg) - fstp st(0) ;; arg - add esp,16 - ret 8 -JIT_Dbl2Lng ENDP - -;*********************************************************************/ -; JIT_Dbl2LngP4x87 - -;Purpose: -; converts a double to a long truncating toward zero (C semantics) -; -; uses stdcall calling conventions -; -; This code is faster on a P4 than the Dbl2Lng code above, but is -; slower on a PIII. Hence we choose this code when on a P4 or above. -; -PUBLIC JIT_Dbl2LngP4x87 -JIT_Dbl2LngP4x87 PROC -arg1 equ <[esp+0Ch]> - - sub esp, 8 ; get some local space - - fld qword ptr arg1 ; fetch arg - fnstcw word ptr arg1 ; store FPCW - movzx eax, word ptr arg1 ; zero extend - wide - or ah, 0Ch ; turn on OE and DE flags - mov dword ptr [esp], eax ; store new FPCW bits - fldcw word ptr [esp] ; reload FPCW with new bits - fistp qword ptr [esp] ; convert - mov eax, dword ptr [esp] ; reload FP result - mov edx, dword ptr [esp+4] ; - fldcw word ptr arg1 ; reload original FPCW value - - add esp, 8 ; restore stack - - ret 8 -JIT_Dbl2LngP4x87 ENDP - -;*********************************************************************/ -; JIT_Dbl2LngSSE3 - ;Purpose: -; converts a double to a long truncating toward zero (C semantics) +; Converts a double to a long truncating toward zero (C semantics) ; -; uses stdcall calling conventions +; Uses stdcall calling convention ; -; This code is faster than the above P4 x87 code for Intel processors -; equal or later than Core2 and Atom that have SSE3 support +; This code is faster than C++ even on modern CPUs despite using the FPU +; but it requires SSE3 ; .686P .XMM -PUBLIC JIT_Dbl2LngSSE3 -JIT_Dbl2LngSSE3 PROC +PUBLIC JIT_Dbl2LngSse3 +JIT_Dbl2LngSse3 PROC arg1 equ <[esp+0Ch]> sub esp, 8 ; get some local space @@ -788,31 +660,9 @@ arg1 equ <[esp+0Ch]> add esp, 8 ; restore stack ret 8 -JIT_Dbl2LngSSE3 ENDP -.586 - -;*********************************************************************/ -; JIT_Dbl2IntSSE2 - -;Purpose: -; converts a double to a long truncating toward zero (C semantics) -; -; uses stdcall calling conventions -; -; This code is even faster than the P4 x87 code for Dbl2LongP4x87, -; but only returns a 32 bit value (only good for int). -; -.686P -.XMM -PUBLIC JIT_Dbl2IntSSE2 -JIT_Dbl2IntSSE2 PROC - $movsd xmm0, [esp+4] - cvttsd2si eax, xmm0 - ret 8 -JIT_Dbl2IntSSE2 ENDP +JIT_Dbl2LngSse3 ENDP .586 - ;*********************************************************************/ ; This is the small write barrier thunk we use when we know the ; ephemeral generation is higher in memory than older generations. @@ -1214,39 +1064,6 @@ JIT_TailCallVSDLeave: JIT_TailCall ENDP - -;------------------------------------------------------------------------------ - -; HCIMPL2_VV(float, JIT_FltRem, float dividend, float divisor) -@JIT_FltRem@8 proc public - fld dword ptr [esp+4] ; divisor - fld dword ptr [esp+8] ; dividend -fremloop: - fprem - fstsw ax - fwait - sahf - jp fremloop ; Continue while the FPU status bit C2 is set - fxch ; swap, so divisor is on top and result is in st(1) - fstp ST(0) ; Pop the divisor from the FP stack - retn 8 ; Return value is in st(0) -@JIT_FltRem@8 endp - -; HCIMPL2_VV(float, JIT_DblRem, float dividend, float divisor) -@JIT_DblRem@16 proc public - fld qword ptr [esp+4] ; divisor - fld qword ptr [esp+12] ; dividend -fremloopd: - fprem - fstsw ax - fwait - sahf - jp fremloopd ; Continue while the FPU status bit C2 is set - fxch ; swap, so divisor is on top and result is in st(1) - fstp ST(0) ; Pop the divisor from the FP stack - retn 16 ; Return value is in st(0) -@JIT_DblRem@16 endp - ;------------------------------------------------------------------------------ ; PatchedCodeStart and PatchedCodeEnd are used to determine bounds of patched code. diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 08360e9ff0c060..8e759e6e257535 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -96,25 +96,6 @@ extern "C" void STDCALL WriteBarrierAssert(BYTE* ptr, Object* obj) #endif // _DEBUG -#ifndef TARGET_UNIX - -HCIMPL1_V(INT32, JIT_Dbl2IntOvf, double val) -{ - FCALL_CONTRACT; - - INT64 ret = HCCALL1_V(JIT_Dbl2Lng, val); - - if (ret != (INT32) ret) - goto THROW; - - return (INT32) ret; - -THROW: - FCThrow(kOverflowException); -} -HCIMPLEND -#endif // TARGET_UNIX - FCDECL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_); @@ -961,30 +942,18 @@ void InitJITHelpers1() JIT_TrialAlloc::Flags flags = GCHeapUtilities::UseThreadAllocationContexts() ? JIT_TrialAlloc::MP_ALLOCATOR : JIT_TrialAlloc::NORMAL; - // Get CPU features and check for SSE2 support. + // Get CPU features and check for SSE3 support. // This code should eventually probably be moved into codeman.cpp, // where we set the cpu feature flags for the JIT based on CPU type and features. int cpuFeatures[4]; __cpuid(cpuFeatures, 1); DWORD dwCPUFeaturesECX = cpuFeatures[2]; - DWORD dwCPUFeaturesEDX = cpuFeatures[3]; - // If bit 26 (SSE2) is set, then we can use the SSE2 flavors - // and faster x87 implementation for the P4 of Dbl2Lng. - if (dwCPUFeaturesEDX & (1<<26)) + if (dwCPUFeaturesECX & 1) // check SSE3 { - SetJitHelperFunction(CORINFO_HELP_DBL2INT, JIT_Dbl2IntSSE2); - if (dwCPUFeaturesECX & 1) // check SSE3 - { - SetJitHelperFunction(CORINFO_HELP_DBL2UINT, JIT_Dbl2LngSSE3); - SetJitHelperFunction(CORINFO_HELP_DBL2LNG, JIT_Dbl2LngSSE3); - } - else - { - SetJitHelperFunction(CORINFO_HELP_DBL2UINT, JIT_Dbl2LngP4x87); // SSE2 only for signed - SetJitHelperFunction(CORINFO_HELP_DBL2LNG, JIT_Dbl2LngP4x87); - } + SetJitHelperFunction(CORINFO_HELP_DBL2UINT, JIT_Dbl2LngSse3); + SetJitHelperFunction(CORINFO_HELP_DBL2LNG, JIT_Dbl2LngSse3); } if (!(TrackAllocationsEnabled() diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 1da02114960293..9ed1b39b48d35d 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -91,6 +91,32 @@ #include +#if !defined(HOST_64BIT) && !defined(TARGET_X86) +/*********************************************************************/ +HCIMPL2_VV(UINT64, JIT_LLsh, UINT64 num, int shift) +{ + FCALL_CONTRACT; + return num << (shift & 0x3F); +} +HCIMPLEND + +/*********************************************************************/ +HCIMPL2_VV(INT64, JIT_LRsh, INT64 num, int shift) +{ + FCALL_CONTRACT; + return num >> (shift & 0x3F); +} +HCIMPLEND + +/*********************************************************************/ +HCIMPL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift) +{ + FCALL_CONTRACT; + return num >> (shift & 0x3F); +} +HCIMPLEND +#endif // !HOST_64BIT && !TARGET_X86 + // // helper macro to multiply two 32-bit uints // @@ -137,117 +163,6 @@ HCIMPL2_VV(INT64, JIT_LMul, INT64 val1, INT64 val2) HCIMPLEND #endif // !TARGET_X86 || TARGET_UNIX -/*********************************************************************/ -HCIMPL2_VV(INT64, JIT_LMulOvf, INT64 val1, INT64 val2) -{ - FCALL_CONTRACT; - - // This short-cut does not actually help since the multiplication - // of two 32-bit signed ints compiles into the call to a slow helper - // if (Is32BitSigned(val1) && Is32BitSigned(val2)) - // return (INT64)(INT32)val1 * (INT64)(INT32)val2; - - INDEBUG(INT64 expected = val1 * val2;) - INT64 ret; - - // Remember the sign of the result - INT32 sign = Hi32Bits(val1) ^ Hi32Bits(val2); - - // Convert to unsigned multiplication - if (val1 < 0) val1 = -val1; - if (val2 < 0) val2 = -val2; - - // Get the upper 32 bits of the numbers - UINT32 val1High = Hi32Bits(val1); - UINT32 val2High = Hi32Bits(val2); - - UINT64 valMid; - - if (val1High == 0) { - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, val1); - } - else { - if (val2High != 0) - goto ThrowExcep; - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, val2); - } - - // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; - - ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid)); - - // check for overflow - if (Hi32Bits(ret) < (UINT32)valMid) - goto ThrowExcep; - - if (sign >= 0) { - // have we spilled into the sign bit? - if (ret < 0) - goto ThrowExcep; - } - else { - ret = -ret; - // have we spilled into the sign bit? - if (ret > 0) - goto ThrowExcep; - } - _ASSERTE(ret == expected); - return ret; - -ThrowExcep: - FCThrow(kOverflowException); -} -HCIMPLEND - -/*********************************************************************/ -HCIMPL2_VV(UINT64, JIT_ULMulOvf, UINT64 val1, UINT64 val2) -{ - FCALL_CONTRACT; - - INDEBUG(UINT64 expected = val1 * val2;) - UINT64 ret; - - // Get the upper 32 bits of the numbers - UINT32 val1High = Hi32Bits(val1); - UINT32 val2High = Hi32Bits(val2); - - UINT64 valMid; - - if (val1High == 0) { - if (val2High == 0) - return Mul32x32To64(val1, val2); - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, val1); - } - else { - if (val2High != 0) - goto ThrowExcep; - // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, val2); - } - - // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; - - ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid)); - - // check for overflow - if (Hi32Bits(ret) < (UINT32)valMid) - goto ThrowExcep; - - _ASSERTE(ret == expected); - return ret; - -ThrowExcep: - FCThrow(kOverflowException); - } -HCIMPLEND - /*********************************************************************/ HCIMPL2(INT32, JIT_Div, INT32 dividend, INT32 divisor) { @@ -450,32 +365,6 @@ HCIMPL2_VV(UINT64, JIT_ULMod, UINT64 dividend, UINT64 divisor) } HCIMPLEND -#if !defined(HOST_64BIT) && !defined(TARGET_X86) -/*********************************************************************/ -HCIMPL2_VV(UINT64, JIT_LLsh, UINT64 num, int shift) -{ - FCALL_CONTRACT; - return num << (shift & 0x3F); -} -HCIMPLEND - -/*********************************************************************/ -HCIMPL2_VV(INT64, JIT_LRsh, INT64 num, int shift) -{ - FCALL_CONTRACT; - return num >> (shift & 0x3F); -} -HCIMPLEND - -/*********************************************************************/ -HCIMPL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift) -{ - FCALL_CONTRACT; - return num >> (shift & 0x3F); -} -HCIMPLEND -#endif // !HOST_64BIT && !TARGET_X86 - #include @@ -488,282 +377,24 @@ HCIMPLEND #include /*********************************************************************/ -// -HCIMPL1_V(double, JIT_ULng2Dbl, UINT64 val) -{ - FCALL_CONTRACT; - - double conv = (double) ((INT64) val); - if (conv < 0) - conv += (4294967296.0 * 4294967296.0); // add 2^64 - _ASSERTE(conv >= 0); - return(conv); -} -HCIMPLEND +// needed for ARM and x86 -/*********************************************************************/ -// needed for ARM and RyuJIT-x86 -HCIMPL1_V(double, JIT_Lng2Dbl, INT64 val) +HCIMPL1_V(INT32, JIT_Dbl2Int, double val) { FCALL_CONTRACT; - return double(val); -} -HCIMPLEND -//-------------------------------------------------------------------------- -template -ftype modftype(ftype value, ftype *iptr); -template <> float modftype(float value, float *iptr) { return modff(value, iptr); } -template <> double modftype(double value, double *iptr) { return modf(value, iptr); } - -// round to nearest, round to even if tied -template -ftype BankersRound(ftype value) -{ - if (value < 0.0) return -BankersRound (-value); - - ftype integerPart; - modftype( value, &integerPart ); - - // if decimal part is exactly .5 - if ((value -(integerPart +0.5)) == 0.0) - { - // round to even - if (fmod(ftype(integerPart), ftype(2.0)) == 0.0) - return integerPart; - - // Else return the nearest even integer - return (ftype)copysign(ceil(fabs(value+0.5)), - value); - } - - // Otherwise round to closest - return (ftype)copysign(floor(fabs(value)+0.5), - value); -} - - -/*********************************************************************/ -// round double to nearest int (as double) -HCIMPL1_V(double, JIT_DoubleRound, double val) -{ - FCALL_CONTRACT; - return BankersRound(val); -} -HCIMPLEND - -/*********************************************************************/ -// round float to nearest int (as float) -HCIMPL1_V(float, JIT_FloatRound, float val) -{ - FCALL_CONTRACT; - return BankersRound(val); -} -HCIMPLEND - -/*********************************************************************/ -// Call fast Dbl2Lng conversion - used by functions below -FORCEINLINE INT64 FastDbl2Lng(double val) -{ -#ifdef TARGET_X86 - FCALL_CONTRACT; - return HCCALL1_V(JIT_Dbl2Lng, val); -#else - FCALL_CONTRACT; - return((__int64) val); -#endif -} - -/*********************************************************************/ -HCIMPL1_V(UINT32, JIT_Dbl2UIntOvf, double val) -{ - FCALL_CONTRACT; - - // Note that this expression also works properly for val = NaN case - if (val > -1.0 && val < 4294967296.0) - return((UINT32)FastDbl2Lng(val)); - - FCThrow(kOverflowException); + return (INT32)val; } HCIMPLEND -/*********************************************************************/ -HCIMPL1_V(UINT64, JIT_Dbl2ULng, double val) -{ - FCALL_CONTRACT; - - const double two63 = 2147483648.0 * 4294967296.0; - UINT64 ret; - if (val < two63) { - ret = FastDbl2Lng(val); - } - else { - // subtract 0x8000000000000000, do the convert then add it back again - ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000); - } - return ret; -} -HCIMPLEND - -/*********************************************************************/ -HCIMPL1_V(UINT64, JIT_Dbl2ULngOvf, double val) -{ - FCALL_CONTRACT; - - const double two64 = 4294967296.0 * 4294967296.0; - // Note that this expression also works properly for val = NaN case - if (val > -1.0 && val < two64) { - const double two63 = 2147483648.0 * 4294967296.0; - UINT64 ret; - if (val < two63) { - ret = FastDbl2Lng(val); - } - else { - // subtract 0x8000000000000000, do the convert then add it back again - ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000); - } -#ifdef _DEBUG - // since no overflow can occur, the value always has to be within 1 - double roundTripVal = HCCALL1_V(JIT_ULng2Dbl, ret); - _ASSERTE(val - 1.0 <= roundTripVal && roundTripVal <= val + 1.0); -#endif // _DEBUG - return ret; - } - - FCThrow(kOverflowException); -} -HCIMPLEND - - -#if !defined(TARGET_X86) || defined(TARGET_UNIX) - HCIMPL1_V(INT64, JIT_Dbl2Lng, double val) { FCALL_CONTRACT; - return((INT64)val); -} -HCIMPLEND - -HCIMPL1_V(int, JIT_Dbl2IntOvf, double val) -{ - FCALL_CONTRACT; - - const double two31 = 2147483648.0; - - // Note that this expression also works properly for val = NaN case - if (val > -two31 - 1 && val < two31) - return((INT32)val); - - FCThrow(kOverflowException); -} -HCIMPLEND - -HCIMPL1_V(INT64, JIT_Dbl2LngOvf, double val) -{ - FCALL_CONTRACT; - - const double two63 = 2147483648.0 * 4294967296.0; - - // Note that this expression also works properly for val = NaN case - // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. - if (val > -two63 - 0x402 && val < two63) - return((INT64)val); - - FCThrow(kOverflowException); -} -HCIMPLEND - -#ifndef TARGET_WINDOWS -namespace -{ - bool isnan(float val) - { - UINT32 bits = *reinterpret_cast(&val); - return (bits & 0x7FFFFFFFU) > 0x7F800000U; - } - bool isnan(double val) - { - UINT64 bits = *reinterpret_cast(&val); - return (bits & 0x7FFFFFFFFFFFFFFFULL) > 0x7FF0000000000000ULL; - } - bool isfinite(float val) - { - UINT32 bits = *reinterpret_cast(&val); - return (~bits & 0x7F800000U) != 0; - } - bool isfinite(double val) - { - UINT64 bits = *reinterpret_cast(&val); - return (~bits & 0x7FF0000000000000ULL) != 0; - } -} -#endif - -HCIMPL2_VV(float, JIT_FltRem, float dividend, float divisor) -{ - FCALL_CONTRACT; - - // - // From the ECMA standard: - // - // If [divisor] is zero or [dividend] is infinity - // the result is NaN. - // If [divisor] is infinity, - // the result is [dividend] (negated for -infinity***). - // - // ***"negated for -infinity" has been removed from the spec - // - - if (divisor==0 || !isfinite(dividend)) - { - UINT32 NaN = CLR_NAN_32; - return *(float *)(&NaN); - } - else if (!isfinite(divisor) && !isnan(divisor)) - { - return dividend; - } - // else... -#if 0 - // COMPILER BUG WITH FMODF() + /Oi, USE FMOD() INSTEAD - return fmodf(dividend,divisor); -#else - return (float)fmod((double)dividend,(double)divisor); -#endif + return (INT64)val; } HCIMPLEND -HCIMPL2_VV(double, JIT_DblRem, double dividend, double divisor) -{ - FCALL_CONTRACT; - - // - // From the ECMA standard: - // - // If [divisor] is zero or [dividend] is infinity - // the result is NaN. - // If [divisor] is infinity, - // the result is [dividend] (negated for -infinity***). - // - // ***"negated for -infinity" has been removed from the spec - // - if (divisor==0 || !isfinite(dividend)) - { - UINT64 NaN = CLR_NAN_64; - return *(double *)(&NaN); - } - else if (!isfinite(divisor) && !isnan(divisor)) - { - return dividend; - } - // else... - return(fmod(dividend,divisor)); -} -HCIMPLEND - -#endif // !TARGET_X86 || TARGET_UNIX - #include diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 05d9365e967964..ae7ec625000dab 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10686,7 +10686,8 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ { _ASSERTE(ppIndirection != NULL); *ppIndirection = &hlpDynamicFuncTable[dynamicFtnNum].pfnHelper; - return NULL; + result = NULL; + goto Exit; } #endif @@ -10695,7 +10696,8 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ LPVOID finalTierAddr = hlpFinalTierAddrTable[dynamicFtnNum]; if (finalTierAddr != NULL) { - return finalTierAddr; + result = finalTierAddr; + goto Exit; } if (dynamicFtnNum == DYNAMIC_CORINFO_HELP_ISINSTANCEOFINTERFACE || @@ -10712,7 +10714,16 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ dynamicFtnNum == DYNAMIC_CORINFO_HELP_LDELEMA_REF || dynamicFtnNum == DYNAMIC_CORINFO_HELP_MEMSET || dynamicFtnNum == DYNAMIC_CORINFO_HELP_MEMZERO || - dynamicFtnNum == DYNAMIC_CORINFO_HELP_MEMCPY) + dynamicFtnNum == DYNAMIC_CORINFO_HELP_MEMCPY || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_DBL2ULNG || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_DBL2INT_OVF || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_DBL2UINT_OVF || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_DBL2LNG_OVF || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_DBL2ULNG_OVF || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_LNG2DBL || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_ULNG2DBL || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_LMUL_OVF || + dynamicFtnNum == DYNAMIC_CORINFO_HELP_ULMUL_OVF) { Precode* pPrecode = Precode::GetPrecodeFromEntryPoint((PCODE)hlpDynamicFuncTable[dynamicFtnNum].pfnHelper); _ASSERTE(pPrecode->GetType() == PRECODE_FIXUP); @@ -10742,13 +10753,15 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ { // Cache it for future uses to avoid taking the lock again. hlpFinalTierAddrTable[dynamicFtnNum] = finalTierAddr; - return finalTierAddr; + result = finalTierAddr; + goto Exit; } } } *ppIndirection = ((FixupPrecode*)pPrecode)->GetTargetSlot(); - return NULL; + result = NULL; + goto Exit; } pfnHelper = hlpDynamicFuncTable[dynamicFtnNum].pfnHelper; @@ -10762,8 +10775,8 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ result = (LPVOID)GetEEFuncEntryPoint(pfnHelper); +Exit: EE_TO_JIT_TRANSITION_LEAF(); - return result; } diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 9b307983a24d0c..fcdcf288f02ba1 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -324,17 +324,7 @@ EXTERN_C FCDECL2(Object*, JIT_NewArr1OBJ_MP_InlineGetThread, CORINFO_CLASS_HANDL #endif // HOST_64BIT EXTERN_C FCDECL2_VV(INT64, JIT_LMul, INT64 val1, INT64 val2); - -EXTERN_C FCDECL1_V(INT64, JIT_Dbl2Lng, double val); -EXTERN_C FCDECL1_V(INT64, JIT_Dbl2IntSSE2, double val); -EXTERN_C FCDECL1_V(INT64, JIT_Dbl2LngP4x87, double val); -EXTERN_C FCDECL1_V(INT64, JIT_Dbl2LngSSE3, double val); -EXTERN_C FCDECL1_V(INT64, JIT_Dbl2LngOvf, double val); - -EXTERN_C FCDECL1_V(INT32, JIT_Dbl2IntOvf, double val); - -EXTERN_C FCDECL2_VV(float, JIT_FltRem, float dividend, float divisor); -EXTERN_C FCDECL2_VV(double, JIT_DblRem, double dividend, double divisor); +EXTERN_C FCDECL1_V(INT64, JIT_Dbl2LngSse3, double val); #ifndef HOST_64BIT #ifdef TARGET_X86 diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 71d73546bc1364..81ad7b87311e6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -440,6 +440,137 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); } + // + // Helpers, those methods are referenced from the JIT + // + + private const double IntMaxValueOffset = 2147483648.0; // 2^31, int.MaxValue + 1 + private const double UIntMaxValueOffset = 4294967296.0; // 2^32, uint.MaxValue + 1 + + // TODO: make the helper saturating when implementing https://github.com/dotnet/runtime/issues/61885 + private static ulong ConvertToUInt64(double value) + { + const double two63 = IntMaxValueOffset * UIntMaxValueOffset; + ulong ret; + // don't remove the double casts, the runtime would call this method recursively without them + if (value < two63) + { + ret = (ulong)(long)value; + } + else + { + // subtract 0x8000000000000000, do the convert then add it back again + ret = (ulong)(long)(value - two63) + 0x8000000000000000UL; + } + return ret; + } + + [StackTraceHidden] + private static int ConvertToInt32Checked(double value) + { + // Note that this expression also works properly for val = NaN case + if (value is > -IntMaxValueOffset - 1 and < IntMaxValueOffset) + { + int ret = (int)value; + // since no overflow can occur, the value always has to be within 1 + Debug.Assert(value - 1.0 <= ret); + Debug.Assert(ret <= value + 1.0); + return ret; + } + + ThrowHelper.ThrowOverflowException(); + return 0; + } + + [StackTraceHidden] + private static uint ConvertToUInt32Checked(double value) + { + // Note that this expression also works properly for val = NaN case + if (value is > -1.0 and < UIntMaxValueOffset) + { + uint ret = (uint)value; + // since no overflow can occur, the value always has to be within 1 + Debug.Assert(value - 1.0 <= ret); + Debug.Assert(ret <= value + 1.0); + return ret; + } + + ThrowHelper.ThrowOverflowException(); + return 0; + } + + [StackTraceHidden] + private static long ConvertToInt64Checked(double value) + { + const double two63 = IntMaxValueOffset * UIntMaxValueOffset; + + // Note that this expression also works properly for val = NaN case + // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. + if (value is > -two63 - 0x402 and < two63) + { + long ret = (long)value; + // since no overflow can occur, the value always has to be within 1 + Debug.Assert(value - 1.0 <= ret); + Debug.Assert(ret <= value + 1.0); + return ret; + } + + ThrowHelper.ThrowOverflowException(); + return 0; + } + + [StackTraceHidden] + private static ulong ConvertToUInt64Checked(double value) + { + const double two64 = UIntMaxValueOffset * UIntMaxValueOffset; + // Note that this expression also works properly for val = NaN case + if (value is > -1.0 and < two64) + { + ulong ret = (ulong)value; + // since no overflow can occur, the value always has to be within 1 + Debug.Assert(value - 1.0 <= ret); + Debug.Assert(ret <= value + 1.0); + return ret; + } + + ThrowHelper.ThrowOverflowException(); + return 0; + } + + private static ref double DoubleConversionTable => ref MemoryMarshal.GetReference([0.0, UIntMaxValueOffset]); + + private static double ConvertFromInt64(long value) + { + uint upper = (uint)(value >>> 32); + uint lower = (uint)value; + double a = (int)upper; + // X86 has no uint -> double casts, we need to do this to avoid recursion here +#if TARGET_X86 + double b = (int)lower; + b += Unsafe.Add(ref DoubleConversionTable, lower >> 31); +#else + double b = lower; +#endif + return a * UIntMaxValueOffset + b; + } + + private static double ConvertFromUInt64(ulong value) + { + uint upper = (uint)(value >> 32); + uint lower = (uint)value; + // X86 has no uint -> double casts, we need to do this to avoid recursion here +#if TARGET_X86 + double a = (int)upper; + double b = (int)lower; + a += Unsafe.Add(ref DoubleConversionTable, upper >> 31); + b += Unsafe.Add(ref DoubleConversionTable, lower >> 31); +#else + double a = upper; + double b = lower; +#endif + return a * UIntMaxValueOffset + b; + } + // // IConvertible implementation // diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index ca170d8c903ed0..f7661fcc93fe0a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -179,6 +179,85 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } + // + // Helpers, those methods are referenced from the JIT + // + + [StackTraceHidden] + private static long MultiplyChecked(long left, long right) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint High32Bits(ulong a) + { + return (uint)(a >> 32); + } + +#if DEBUG + long result = left * right; +#endif + + // Remember the sign of the result + int sign = (int)(High32Bits((ulong)left) ^ High32Bits((ulong)right)); + + // Convert to unsigned multiplication + if (left < 0) + left = -left; + if (right < 0) + right = -right; + + // Get the upper 32 bits of the numbers + uint val1High = High32Bits((ulong)left); + uint val2High = High32Bits((ulong)right); + + ulong valMid; + + if (val1High == 0) + { + // Compute the 'middle' bits of the long multiplication + valMid = Math.BigMul(val2High, (uint)left); + } + else + { + if (val2High != 0) + goto Overflow; + // Compute the 'middle' bits of the long multiplication + valMid = Math.BigMul(val1High, (uint)right); + } + + // See if any bits after bit 32 are set + if (High32Bits(valMid) != 0) + goto Overflow; + + long ret = (long)(Math.BigMul((uint)left, (uint)right) + (valMid << 32)); + + // check for overflow + if (High32Bits((ulong)ret) < (uint)valMid) + goto Overflow; + + if (sign >= 0) + { + // have we spilled into the sign bit? + if (ret < 0) + goto Overflow; + } + else + { + ret = -ret; + // have we spilled into the sign bit? + if (ret > 0) + goto Overflow; + } + +#if DEBUG + Debug.Assert(ret == result, $"Multiply overflow got: {ret}, expected: {result}"); +#endif + return ret; + + Overflow: + ThrowHelper.ThrowOverflowException(); + return 0; + } + // // IConvertible implementation // diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index dc12c5c006a82b..f3ff6903aa24aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; @@ -176,6 +177,58 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } + // + // Helpers, those methods are referenced from the JIT + // + + [StackTraceHidden] + private static ulong MultiplyChecked(ulong left, ulong right) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint High32Bits(ulong a) + { + return (uint)(a >> 32); + } + + // Get the upper 32 bits of the numbers + uint val1High = High32Bits(left); + uint val2High = High32Bits(right); + + ulong valMid; + + if (val1High == 0) + { + if (val2High == 0) + return Math.BigMul((uint)left, (uint)right); + // Compute the 'middle' bits of the long multiplication + valMid = Math.BigMul(val2High, (uint)left); + } + else + { + if (val2High != 0) + goto Overflow; + // Compute the 'middle' bits of the long multiplication + valMid = Math.BigMul(val1High, (uint)right); + } + + // See if any bits after bit 32 are set + if (High32Bits(valMid) != 0) + goto Overflow; + + ulong ret = Math.BigMul((uint)left, (uint)right) + (valMid << 32); + + // check for overflow + if (High32Bits(ret) < (uint)valMid) + goto Overflow; + + Debug.Assert(ret == left * right, $"Multiply overflow got: {ret}, expected: {left * right}"); + return ret; + + Overflow: + ThrowHelper.ThrowOverflowException(); + return 0; + } + // // IConvertible implementation //