From d300694b3c108dfb6bd4950ee9646b729992b4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Thu, 28 Mar 2024 00:18:00 +0100 Subject: [PATCH 1/6] Cleanup NativeAOT math helpers --- .../Runtime/CompilerHelpers/MathHelpers.cs | 363 ++++++------------ 1 file changed, 118 insertions(+), 245 deletions(-) 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..4a3c9b1bd7fa7e 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 @@ -2,18 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Runtime; 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. /// + [StackTraceHidden] internal static partial class MathHelpers { #if !TARGET_64BIT @@ -23,360 +22,234 @@ internal static partial class MathHelpers 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) + public static long LMulOvf(long left, long right) { - long ret; + [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)(Hi32Bits(i) ^ Hi32Bits(j)); + int sign = (int)(High32Bits((ulong)left) ^ High32Bits((ulong)right)); // Convert to unsigned multiplication - if (i < 0) i = -i; - if (j < 0) j = -j; + if (left < 0) + left = -left; + if (right < 0) + right = -right; // Get the upper 32 bits of the numbers - uint val1High = Hi32Bits(i); - uint val2High = Hi32Bits(j); + uint val1High = High32Bits((ulong)left); + uint val2High = High32Bits((ulong)right); ulong valMid; if (val1High == 0) { // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (uint)i); + valMid = Math.BigMul(val2High, (uint)left); } else { if (val2High != 0) - goto ThrowExcep; + goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (uint)j); + valMid = Math.BigMul(val1High, (uint)right); } // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; + if (High32Bits(valMid) != 0) + goto Overflow; - ret = (long)(Mul32x32To64((uint)i, (uint)j) + (valMid << 32)); + long ret = (long)(Math.BigMul((uint)left, (uint)right) + (valMid << 32)); // check for overflow - if (Hi32Bits(ret) < (uint)valMid) - goto ThrowExcep; + if (High32Bits((ulong)ret) < (uint)valMid) + goto Overflow; if (sign >= 0) { // have we spilled into the sign bit? if (ret < 0) - goto ThrowExcep; + goto Overflow; } else { ret = -ret; // have we spilled into the sign bit? if (ret > 0) - goto ThrowExcep; + goto Overflow; } + +#if DEBUG + Debug.Assert(ret == result, $"Multiply overflow got: {ret}, expected: {result}"); +#endif return ret; - ThrowExcep: - return ThrowLngOvf(); + Overflow: + ThrowHelper.ThrowOverflowException(); + return 0; } [RuntimeExport("ULMulOvf")] - public static ulong ULMulOvf(ulong i, ulong j) + public static ulong ULMulOvf(ulong left, ulong right) { - ulong ret; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint High32Bits(ulong a) + { + return (uint)(a >> 32); + } // Get the upper 32 bits of the numbers - uint val1High = Hi32Bits(i); - uint val2High = Hi32Bits(j); + uint val1High = High32Bits(left); + uint val2High = High32Bits(right); ulong valMid; if (val1High == 0) { if (val2High == 0) - return Mul32x32To64((uint)i, (uint)j); + return Math.BigMul((uint)left, (uint)right); // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (uint)i); + valMid = Math.BigMul(val2High, (uint)left); } else { if (val2High != 0) - goto ThrowExcep; + goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (uint)j); + valMid = Math.BigMul(val1High, (uint)right); } // See if any bits after bit 32 are set - if (Hi32Bits(valMid) != 0) - goto ThrowExcep; + if (High32Bits(valMid) != 0) + goto Overflow; - ret = Mul32x32To64((uint)i, (uint)j) + (valMid << 32); + ulong ret = Math.BigMul((uint)left, (uint)right) + (valMid << 32); // check for overflow - if (Hi32Bits(ret) < (uint)valMid) - goto ThrowExcep; + if (High32Bits(ret) < (uint)valMid) + goto Overflow; + + Debug.Assert(ret == left * right, $"Multiply overflow got: {ret}, expected: {left * right}"); return ret; - ThrowExcep: - return ThrowULngOvf(); + Overflow: + ThrowHelper.ThrowOverflowException(); + return 0; } [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); - } + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - [LibraryImport(RuntimeLibrary)] - [SuppressGCTransition] - private static partial long RhpLMod(long i, long j); - - 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 + private static partial ulong RhpULDiv(ulong dividend, ulong divisor); - [RuntimeExport("Dbl2IntOvf")] - public static int Dbl2IntOvf(double val) + public static ulong ULDiv(ulong dividend, ulong divisor) { - const double two31 = 2147483648.0; + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); - // Note that this expression also works properly for val = NaN case - if (val > -two31 - 1 && val < two31) - return unchecked((int)val); - - return ThrowIntOvf(); + return RhpULDiv(dividend, divisor); } - [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(); - } - - [RuntimeExport("Dbl2ULngOvf")] - public static ulong Dbl2ULngOvf(double val) - { - 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); - - return ThrowULngOvf(); - } - - [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); - } + if (divisor == 0) + ThrowHelper.ThrowDivideByZeroException(); + if (divisor == -1 && dividend == int.MinValue) + ThrowHelper.ThrowOverflowException(); - [RuntimeImport(RuntimeLibrary, "RhpUDiv")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern uint RhpUDiv(uint i, uint j); - - 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 } } From 269896d826df811cff487167b68147097fe431ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Fri, 29 Mar 2024 03:33:37 +0100 Subject: [PATCH 2/6] Restore checked cast helpers --- .../Runtime/CompilerHelpers/MathHelpers.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) 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 4a3c9b1bd7fa7e..92fc3e9be3cddc 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 @@ -15,6 +15,81 @@ namespace Internal.Runtime.CompilerHelpers [StackTraceHidden] internal static partial class MathHelpers { + private const double IntMaxValueOffset = 2147483648.0; // 2^31, int.MaxValue + 1 + private const double UIntMaxValueOffset = 4294967296.0; // 2^32, uint.MaxValue + 1 + + [RuntimeExport("Dbl2IntOvf")] + public static int Dbl2IntOvf(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; + } + + [RuntimeExport("Dbl2UIntOvf")] + public static uint Dbl2UIntOvf(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; + } + + [RuntimeExport("Dbl2LngOvf")] + public static long Dbl2LngOvf(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; + } + + [RuntimeExport("Dbl2ULngOvf")] + public static ulong Dbl2ULngOvf(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; + } + #if !TARGET_64BIT // // 64-bit checked multiplication for 32-bit platforms From e2e17253e8fda0813b3f55bdd949737c1b82bbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 30 Mar 2024 02:42:32 +0100 Subject: [PATCH 3/6] Review --- .../Runtime/CompilerHelpers/MathHelpers.cs | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) 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 92fc3e9be3cddc..00a30a839408be 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 @@ -24,11 +24,7 @@ public static int Dbl2IntOvf(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; + return (int)value; } ThrowHelper.ThrowOverflowException(); @@ -41,11 +37,7 @@ public static uint Dbl2UIntOvf(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; + return (uint)value; } ThrowHelper.ThrowOverflowException(); @@ -61,11 +53,7 @@ public static long Dbl2LngOvf(double value) // 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; + return (long)value; } ThrowHelper.ThrowOverflowException(); @@ -79,11 +67,7 @@ public static ulong Dbl2ULngOvf(double value) // 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; + return (ulong)value; } ThrowHelper.ThrowOverflowException(); @@ -128,21 +112,21 @@ static uint High32Bits(ulong a) if (val1High == 0) { // Compute the 'middle' bits of the long multiplication - valMid = Math.BigMul(val2High, (uint)left); + valMid = (ulong)val2High * (uint)left; } else { if (val2High != 0) goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = Math.BigMul(val1High, (uint)right); + valMid = (ulong)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)); + long ret = (long)(((ulong)(uint)left * (uint)right) + (valMid << 32)); // check for overflow if (High32Bits((ulong)ret) < (uint)valMid) @@ -190,23 +174,23 @@ static uint High32Bits(ulong a) if (val1High == 0) { if (val2High == 0) - return Math.BigMul((uint)left, (uint)right); + return (ulong)(uint)left * (uint)right; // Compute the 'middle' bits of the long multiplication - valMid = Math.BigMul(val2High, (uint)left); + valMid = (ulong)val2High * (uint)left; } else { if (val2High != 0) goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = Math.BigMul(val1High, (uint)right); + valMid = (ulong)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); + ulong ret = ((ulong)(uint)left * (uint)right) + (valMid << 32); // check for overflow if (High32Bits(ret) < (uint)valMid) From ab3c9c903a400e22abfc275422e1cdd62f9b6973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 31 Mar 2024 23:14:33 +0200 Subject: [PATCH 4/6] Cleanup helpers --- .../Runtime/CompilerHelpers/MathHelpers.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 00a30a839408be..ff32cf2a737aea 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 @@ -81,15 +81,21 @@ public static ulong Dbl2ULngOvf(double value) private const string RuntimeLibrary = "*"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint High32Bits(ulong a) + { + return (uint)(a >> 32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong BigMul(uint left, uint right) + { + return (ulong)left * right; + } + [RuntimeExport("LMulOvf")] public static long LMulOvf(long left, long right) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static uint High32Bits(ulong a) - { - return (uint)(a >> 32); - } - #if DEBUG long result = left * right; #endif @@ -112,21 +118,21 @@ static uint High32Bits(ulong a) if (val1High == 0) { // Compute the 'middle' bits of the long multiplication - valMid = (ulong)val2High * (uint)left; + valMid = BigMul(val2High, (uint)left); } else { if (val2High != 0) goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = (ulong)val1High * (uint)right; + valMid = BigMul(val1High, (uint)right); } // See if any bits after bit 32 are set if (High32Bits(valMid) != 0) goto Overflow; - long ret = (long)(((ulong)(uint)left * (uint)right) + (valMid << 32)); + long ret = (long)(BigMul((uint)left, (uint)right) + (valMid << 32)); // check for overflow if (High32Bits((ulong)ret) < (uint)valMid) @@ -159,12 +165,6 @@ static uint High32Bits(ulong a) [RuntimeExport("ULMulOvf")] public static ulong ULMulOvf(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); @@ -176,21 +176,21 @@ static uint High32Bits(ulong a) if (val2High == 0) return (ulong)(uint)left * (uint)right; // Compute the 'middle' bits of the long multiplication - valMid = (ulong)val2High * (uint)left; + valMid = BigMul(val2High, (uint)left); } else { if (val2High != 0) goto Overflow; // Compute the 'middle' bits of the long multiplication - valMid = (ulong)val1High * (uint)right; + valMid = BigMul(val1High, (uint)right); } // See if any bits after bit 32 are set if (High32Bits(valMid) != 0) goto Overflow; - ulong ret = ((ulong)(uint)left * (uint)right) + (valMid << 32); + ulong ret = BigMul((uint)left, (uint)right) + (valMid << 32); // check for overflow if (High32Bits(ret) < (uint)valMid) From c57595d6a27c5e72d09c9b04ee5281bbc4407aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Fri, 5 Apr 2024 03:45:52 +0200 Subject: [PATCH 5/6] Rename variables --- .../Runtime/CompilerHelpers/MathHelpers.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 ff32cf2a737aea..3e6544308f7b0c 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 @@ -13,16 +13,16 @@ namespace Internal.Runtime.CompilerHelpers /// Math helpers for generated code. The helpers here are referenced by the runtime. /// [StackTraceHidden] - internal static partial class MathHelpers + internal static class MathHelpers { - private const double IntMaxValueOffset = 2147483648.0; // 2^31, int.MaxValue + 1 - private const double UIntMaxValueOffset = 4294967296.0; // 2^32, uint.MaxValue + 1 + private const double Int32MaxValueOffset = (double)int.MaxValue + 1; + private const double UInt32MaxValueOffset = (double)uint.MaxValue + 1; [RuntimeExport("Dbl2IntOvf")] public static int Dbl2IntOvf(double value) { // Note that this expression also works properly for val = NaN case - if (value is > -IntMaxValueOffset - 1 and < IntMaxValueOffset) + if (value is > -Int32MaxValueOffset - 1 and < Int32MaxValueOffset) { return (int)value; } @@ -35,7 +35,7 @@ public static int Dbl2IntOvf(double value) public static uint Dbl2UIntOvf(double value) { // Note that this expression also works properly for val = NaN case - if (value is > -1.0 and < UIntMaxValueOffset) + if (value is > -1.0 and < UInt32MaxValueOffset) { return (uint)value; } @@ -47,7 +47,7 @@ public static uint Dbl2UIntOvf(double value) [RuntimeExport("Dbl2LngOvf")] public static long Dbl2LngOvf(double value) { - const double two63 = IntMaxValueOffset * UIntMaxValueOffset; + const double two63 = Int32MaxValueOffset * UInt32MaxValueOffset; // 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. @@ -63,7 +63,7 @@ public static long Dbl2LngOvf(double value) [RuntimeExport("Dbl2ULngOvf")] public static ulong Dbl2ULngOvf(double value) { - const double two64 = UIntMaxValueOffset * UIntMaxValueOffset; + const double two64 = UInt32MaxValueOffset * UInt32MaxValueOffset; // Note that this expression also works properly for val = NaN case if (value is > -1.0 and < two64) { From ab9a68b99a7339f7f520e30c261f4be15c623598 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 4 Apr 2024 19:32:03 -0700 Subject: [PATCH 6/6] Update src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs --- .../src/Internal/Runtime/CompilerHelpers/MathHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3e6544308f7b0c..42485ea3ef63cd 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 @@ -13,7 +13,7 @@ namespace Internal.Runtime.CompilerHelpers /// Math helpers for generated code. The helpers here are referenced by the runtime. /// [StackTraceHidden] - internal static class MathHelpers + internal static partial class MathHelpers { private const double Int32MaxValueOffset = (double)int.MaxValue + 1; private const double UInt32MaxValueOffset = (double)uint.MaxValue + 1;