From 047d4ceed434f1d5dd83a67481e63f7f555f209d Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 11:35:47 -0700 Subject: [PATCH 1/8] Moving parts of `System.MathF` to be shared with CoreRT. --- src/classlibnative/float/floatsingle.cpp | 37 ++-- src/classlibnative/inc/floatsingle.h | 4 +- src/jit/importer.cpp | 16 +- src/jit/namedintrinsiclist.h | 3 +- src/mscorlib/System.Private.CoreLib.csproj | 2 +- .../System.Private.CoreLib.Shared.projitems | 1 + src/mscorlib/{src => shared}/System/MathF.cs | 170 ++++++++---------- src/mscorlib/src/System/MathF.CoreCLR.cs | 76 ++++++++ src/vm/ecalllist.h | 4 +- 9 files changed, 183 insertions(+), 130 deletions(-) rename src/mscorlib/{src => shared}/System/MathF.cs (54%) create mode 100644 src/mscorlib/src/System/MathF.CoreCLR.cs diff --git a/src/classlibnative/float/floatsingle.cpp b/src/classlibnative/float/floatsingle.cpp index ba48aea70985..6e5021aedc54 100644 --- a/src/classlibnative/float/floatsingle.cpp +++ b/src/classlibnative/float/floatsingle.cpp @@ -130,6 +130,15 @@ FCIMPL1_V(float, COMSingle::Floor, float x) return (float)floorf(x); FCIMPLEND +/*=====================================FMod===================================== +** +==============================================================================*/ +FCIMPL2_VV(float, COMSingle::FMod, float x, float y) + FCALL_CONTRACT; + + return (float)fmodf(x, y); +FCIMPLEND + /*=====================================Log====================================== ** ==============================================================================*/ @@ -151,10 +160,10 @@ FCIMPLEND /*=====================================ModF===================================== ** ==============================================================================*/ -FCIMPL1(float, COMSingle::ModF, float* iptr) +FCIMPL2_VV(float, COMSingle::ModF, float x, float* intptr) FCALL_CONTRACT; - return (float)modff(*iptr, iptr); + return (float)modff(x, intptr); FCIMPLEND /*=====================================Pow====================================== @@ -166,30 +175,6 @@ FCIMPL2_VV(float, COMSingle::Pow, float x, float y) return (float)powf(x, y); FCIMPLEND -/*====================================Round===================================== -** -==============================================================================*/ -FCIMPL1_V(float, COMSingle::Round, float x) - FCALL_CONTRACT; - - // If the number has no fractional part do nothing - // This shortcut is necessary to workaround precision loss in borderline cases on some platforms - if (x == (float)((INT32)x)) { - return x; - } - - // We had a number that was equally close to 2 integers. - // We need to return the even one. - - float flrTempVal = floorf(x + 0.5f); - - if ((x == (floorf(x) + 0.5f)) && (fmod(flrTempVal, 2.0f) != 0)) { - flrTempVal -= 1.0f; - } - - return _copysignf(flrTempVal, x); -FCIMPLEND - /*=====================================Sin====================================== ** ==============================================================================*/ diff --git a/src/classlibnative/inc/floatsingle.h b/src/classlibnative/inc/floatsingle.h index 8296e2d37a77..912278c913e1 100644 --- a/src/classlibnative/inc/floatsingle.h +++ b/src/classlibnative/inc/floatsingle.h @@ -20,11 +20,11 @@ class COMSingle { FCDECL1_V(static float, Cosh, float x); FCDECL1_V(static float, Exp, float x); FCDECL1_V(static float, Floor, float x); + FCDECL2_VV(static float, FMod, float x, float y); FCDECL1_V(static float, Log, float x); FCDECL1_V(static float, Log10, float x); - FCDECL1(static float, ModF, float* iptr); + FCDECL2_VV(static float, ModF, float x, float* intptr); FCDECL2_VV(static float, Pow, float x, float y); - FCDECL1_V(static float, Round, float x); FCDECL1_V(static float, Sin, float x); FCDECL1_V(static float, Sinh, float x); FCDECL1_V(static float, Sqrt, float x); diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index ddee0aba5b1e..643d48068197 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3897,11 +3897,21 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) if ((namespaceName != nullptr) && strcmp(namespaceName, "System") == 0) { - if ((className != nullptr) && strcmp(className, "Enum") == 0) + if (className != nullptr) { - if ((methodName != nullptr) && strcmp(methodName, "HasFlag") == 0) + if (strcmp(className, "Enum") == 0) { - result = NI_Enum_HasFlag; + if ((methodName != nullptr) && strcmp(methodName, "HasFlag") == 0) + { + result = NI_Enum_HasFlag; + } + } + else if (strcmp(className, "MathF") == 0) + { + if ((methodName != nullptr) && strcmp(methodName, "Round") == 0) + { + result = NI_MathF_Round; + } } } } diff --git a/src/jit/namedintrinsiclist.h b/src/jit/namedintrinsiclist.h index cf81afc119da..facb3cf04555 100644 --- a/src/jit/namedintrinsiclist.h +++ b/src/jit/namedintrinsiclist.h @@ -10,7 +10,8 @@ enum NamedIntrinsic { NI_Illegal = 0, - NI_Enum_HasFlag = 1 + NI_Enum_HasFlag = 1, + NI_MathF_Round = 2 }; #endif // _NAMEDINTRINSICLIST_H_ diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj index 534fc88a61fb..90294fd527a7 100644 --- a/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/mscorlib/System.Private.CoreLib.csproj @@ -330,7 +330,7 @@ <Compile Include="$(BclSourcesRoot)\System\IntPtr.cs" /> <Compile Include="$(BclSourcesRoot)\System\Internal.cs" /> <Compile Include="$(BclSourcesRoot)\System\Math.cs" /> - <Compile Include="$(BclSourcesRoot)\System\MathF.cs" /> + <Compile Include="$(BclSourcesRoot)\System\MathF.CoreCLR.cs" /> <Compile Include="$(BclSourcesRoot)\System\mda.cs" /> <Compile Include="$(BclSourcesRoot)\System\MissingFieldException.cs" /> <Compile Include="$(BclSourcesRoot)\System\MissingMemberException.cs" /> diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index 095d80963afb..faf6dcca05ea 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -198,6 +198,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Int32.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\MathF.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MemberAccessException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Memory.cs" /> diff --git a/src/mscorlib/src/System/MathF.cs b/src/mscorlib/shared/System/MathF.cs similarity index 54% rename from src/mscorlib/src/System/MathF.cs rename to src/mscorlib/shared/System/MathF.cs index 60669a456152..19089dd220a9 100644 --- a/src/mscorlib/src/System/MathF.cs +++ b/src/mscorlib/shared/System/MathF.cs @@ -10,18 +10,17 @@ //This class contains only static members and doesn't require serialization. -using System; +using System.Diagnostics.Contracts; using System.Runtime; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - public static class MathF + public static partial class MathF { - private static float singleRoundLimit = 1e8f; + public const float E = 2.71828183f; + + public const float PI = 3.14159265f; private const int maxRoundingDigits = 6; @@ -30,38 +29,13 @@ public static class MathF 1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f }; - public const float PI = 3.14159265f; - - public const float E = 2.71828183f; - - public static float Abs(float x) => Math.Abs(x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Acos(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Asin(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Atan(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Atan2(float y, float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Ceiling(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Cos(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Cosh(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Exp(float x); + private static float singleRoundLimit = 1e8f; - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Floor(float x); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Abs(float x) + { + return Math.Abs(x); + } public static float IEEERemainder(float x, float y) { @@ -114,9 +88,6 @@ public static float IEEERemainder(float x, float y) } } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Log(float x); - public static float Log(float x, float y) { if (float.IsNaN(x)) @@ -142,31 +113,55 @@ public static float Log(float x, float y) return Log(x) / Log(y); } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Log10(float x); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Max(float x, float y) + { + return Math.Max(x, y); + } - public static float Max(float x, float y) => Math.Max(x, y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Min(float x, float y) + { + return Math.Min(x, y); + } + + [Intrinsic] + public static float Round(float x) + { + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms - public static float Min(float x, float y) => Math.Min(x, y); + if (x == (float)((int)x)) + { + return x; + } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Pow(float x, float y); + // We had a number that was equally close to 2 integers. + // We need to return the even one. - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Round(float x); + float flrTempVal = Floor(x + 0.5f); - public static float Round(float x, int digits) - { - if ((digits < 0) || (digits > maxRoundingDigits)) + if ((x == (Floor(x) + 0.5f)) && (FMod(flrTempVal, 2.0f) != 0)) { - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + flrTempVal -= 1.0f; } - Contract.EndContractBlock(); - return InternalRound(x, digits, MidpointRounding.ToEven); + return CopySign(flrTempVal, x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, int digits) + { + return Round(x, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, MidpointRounding mode) + { + return Round(x, 0, mode); } - public static float Round(float x, int digits, MidpointRounding mode) + public static unsafe float Round(float x, int digits, MidpointRounding mode) { if ((digits < 0) || (digits > maxRoundingDigits)) { @@ -179,41 +174,6 @@ public static float Round(float x, int digits, MidpointRounding mode) } Contract.EndContractBlock(); - return InternalRound(x, digits, mode); - } - - public static float Round(float x, MidpointRounding mode) - { - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode)); - } - Contract.EndContractBlock(); - - return InternalRound(x, 0, mode); - } - - public static int Sign(float x) => Math.Sign(x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Sin(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Sinh(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Sqrt(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Tan(float x); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float Tanh(float x); - - public static float Truncate(float x) => InternalTruncate(x); - - private static unsafe float InternalRound(float x, int digits, MidpointRounding mode) - { if (Abs(x) < singleRoundLimit) { var power10 = roundPower10Single[digits]; @@ -222,7 +182,7 @@ private static unsafe float InternalRound(float x, int digits, MidpointRounding if (mode == MidpointRounding.AwayFromZero) { - var fraction = SplitFractionSingle(&x); + var fraction = ModF(x, &x); if (Abs(fraction) >= 0.5f) { @@ -240,13 +200,33 @@ private static unsafe float InternalRound(float x, int digits, MidpointRounding return x; } - private unsafe static float InternalTruncate(float x) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(float x) + { + return Math.Sign(x); + } + + public static unsafe float Truncate(float x) { - SplitFractionSingle(&x); + ModF(x, &x); return x; } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static unsafe extern float SplitFractionSingle(float* x); + private static unsafe float CopySign(float x, float y) + { + var xbits = BitConverter.SingleToInt32Bits(x); + var ybits = BitConverter.SingleToInt32Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 31) != 0) + { + return BitConverter.Int32BitsToSingle(xbits ^ int.MinValue); + } + + return x; + } } } diff --git a/src/mscorlib/src/System/MathF.CoreCLR.cs b/src/mscorlib/src/System/MathF.CoreCLR.cs new file mode 100644 index 000000000000..0e4dcee73785 --- /dev/null +++ b/src/mscorlib/src/System/MathF.CoreCLR.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Purpose: Some single-precision floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class MathF + { + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Acos(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Asin(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Atan(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Atan2(float y, float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Ceiling(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Cos(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Cosh(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Exp(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Floor(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Log(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Log10(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Pow(float x, float y); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Sin(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Sinh(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Sqrt(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Tan(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Tanh(float x); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern float FMod(float x, float y); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe float ModF(float x, float* intptr); + } +} diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 6997d0f65cfc..7a1c5b237d80 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -674,13 +674,13 @@ FCFuncStart(gMathFFuncs) FCIntrinsic("Cosh", COMSingle::Cosh, CORINFO_INTRINSIC_Cosh) FCIntrinsic("Exp", COMSingle::Exp, CORINFO_INTRINSIC_Exp) FCIntrinsic("Floor", COMSingle::Floor, CORINFO_INTRINSIC_Floor) + FCFuncElement("FMod", COMSingle::FMod) FCFuncElement("Log", COMSingle::Log) FCIntrinsic("Log10", COMSingle::Log10, CORINFO_INTRINSIC_Log10) + FCFuncElement("ModF", COMSingle::ModF) FCIntrinsic("Pow", COMSingle::Pow, CORINFO_INTRINSIC_Pow) - FCIntrinsic("Round", COMSingle::Round, CORINFO_INTRINSIC_Round) FCIntrinsic("Sin", COMSingle::Sin, CORINFO_INTRINSIC_Sin) FCIntrinsic("Sinh", COMSingle::Sinh, CORINFO_INTRINSIC_Sinh) - FCFuncElement("SplitFractionSingle", COMSingle::ModF) FCIntrinsic("Sqrt", COMSingle::Sqrt, CORINFO_INTRINSIC_Sqrt) FCIntrinsic("Tan", COMSingle::Tan, CORINFO_INTRINSIC_Tan) FCIntrinsic("Tanh", COMSingle::Tanh, CORINFO_INTRINSIC_Tanh) From d38374dafe7ca8e708624bc5477d07a00d07167c Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 12:57:39 -0700 Subject: [PATCH 2/8] Moving parts of `System.Math` to be shared with CoreRT. --- src/classlibnative/float/floatdouble.cpp | 37 +- src/classlibnative/inc/floatdouble.h | 4 +- src/jit/importer.cpp | 7 + src/jit/namedintrinsiclist.h | 3 +- .../System.Private.CoreLib.Shared.projitems | 1 + src/mscorlib/shared/System/Math.cs | 831 ++++++++++++++++++ src/mscorlib/src/System/Math.cs | 715 +-------------- src/vm/ecalllist.h | 4 +- 8 files changed, 894 insertions(+), 708 deletions(-) create mode 100644 src/mscorlib/shared/System/Math.cs diff --git a/src/classlibnative/float/floatdouble.cpp b/src/classlibnative/float/floatdouble.cpp index ba90a57f888a..419a88987123 100644 --- a/src/classlibnative/float/floatdouble.cpp +++ b/src/classlibnative/float/floatdouble.cpp @@ -132,6 +132,15 @@ FCIMPL1_V(double, COMDouble::Floor, double x) return (double)floor(x); FCIMPLEND +/*=====================================FMod===================================== +** +==============================================================================*/ +FCIMPL2_VV(double, COMDouble::FMod, double x, double y) + FCALL_CONTRACT; + + return (double)fmod(x, y); +FCIMPLEND + /*=====================================Log====================================== ** ==============================================================================*/ @@ -153,10 +162,10 @@ FCIMPLEND /*=====================================ModF===================================== ** ==============================================================================*/ -FCIMPL1(double, COMDouble::ModF, double* iptr) +FCIMPL2_VV(double, COMDouble::ModF, double x, double* intptr) FCALL_CONTRACT; - return (double)modf(*iptr, iptr); + return (double)modf(x, intptr); FCIMPLEND /*=====================================Pow====================================== @@ -168,30 +177,6 @@ FCIMPL2_VV(double, COMDouble::Pow, double x, double y) return (double)pow(x, y); FCIMPLEND -/*====================================Round===================================== -** -==============================================================================*/ -FCIMPL1_V(double, COMDouble::Round, double x) - FCALL_CONTRACT; - - // If the number has no fractional part do nothing - // This shortcut is necessary to workaround precision loss in borderline cases on some platforms - if (x == (double)((INT64)x)) { - return x; - } - - // We had a number that was equally close to 2 integers. - // We need to return the even one. - - double flrTempVal = floor(x + 0.5); - - if ((x == (floor(x) + 0.5)) && (fmod(flrTempVal, 2.0) != 0)) { - flrTempVal -= 1.0; - } - - return _copysign(flrTempVal, x); -FCIMPLEND - /*=====================================Sin====================================== ** ==============================================================================*/ diff --git a/src/classlibnative/inc/floatdouble.h b/src/classlibnative/inc/floatdouble.h index 16403c1f368e..0d195e1ace09 100644 --- a/src/classlibnative/inc/floatdouble.h +++ b/src/classlibnative/inc/floatdouble.h @@ -20,11 +20,11 @@ class COMDouble { FCDECL1_V(static double, Cosh, double x); FCDECL1_V(static double, Exp, double x); FCDECL1_V(static double, Floor, double x); + FCDECL2_VV(static double, FMod, double x, double y); FCDECL1_V(static double, Log, double x); FCDECL1_V(static double, Log10, double x); - FCDECL1(static double, ModF, double* iptr); + FCDECL2_VV(static double, ModF, double x, double* intptr); FCDECL2_VV(static double, Pow, double x, double y); - FCDECL1_V(static double, Round, double x); FCDECL1_V(static double, Sin, double x); FCDECL1_V(static double, Sinh, double x); FCDECL1_V(static double, Sqrt, double x); diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 643d48068197..c6bdb0d53d51 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3913,6 +3913,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_MathF_Round; } } + else if (strcmp(clasName, "Math") == 0) + { + if ((methodName != nullptr) && strcmp(methodName, "Round") == 0) + { + result = NI_Math_Round; + } + } } } diff --git a/src/jit/namedintrinsiclist.h b/src/jit/namedintrinsiclist.h index facb3cf04555..2cbdb4596546 100644 --- a/src/jit/namedintrinsiclist.h +++ b/src/jit/namedintrinsiclist.h @@ -11,7 +11,8 @@ enum NamedIntrinsic { NI_Illegal = 0, NI_Enum_HasFlag = 1, - NI_MathF_Round = 2 + NI_MathF_Round = 2, + NI_Math_Round = 3 }; #endif // _NAMEDINTRINSICLIST_H_ diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index faf6dcca05ea..9dcde8243629 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -198,6 +198,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Int32.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Math.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MathF.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MemberAccessException.cs" /> diff --git a/src/mscorlib/shared/System/Math.cs b/src/mscorlib/shared/System/Math.cs new file mode 100644 index 000000000000..8c15715a8693 --- /dev/null +++ b/src/mscorlib/shared/System/Math.cs @@ -0,0 +1,831 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Some floating-point math operations +** +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Diagnostics.Contracts; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + public static partial class Math + { + public const double E = 2.7182818284590452354; + + public const double PI = 3.14159265358979323846; + + private const int maxRoundingDigits = 15; + + private static double doubleRoundLimit = 1e16d; + + // This table is required for the Round function which can specify the number of digits to round to + private static double[] roundPower10Double = new double[] { + 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, + 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 + }; + + public static short Abs(short value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + public static int Abs(int value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + public static long Abs(long value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + [CLSCompliant(false)] + public static sbyte Abs(sbyte value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Abs(decimal value) + { + return decimal.Abs(value); + } + + public static long BigMul(int a, int b) + { + return ((long)a) * b; + } + + public static int DivRem(int a, int b, out int result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + int div = a / b; + result = a - (div * b); + return div; + } + + public static long DivRem(long a, long b, out long result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + long div = a / b; + result = a - (div * b); + return div; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Ceiling(decimal d) + { + return decimal.Ceiling(d); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte min, byte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Clamp(decimal value, decimal min, decimal max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(double value, double min, double max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Clamp(short value, short min, short max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int value, int min, int max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Clamp(long value, long min, long max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte Clamp(sbyte value, sbyte min, sbyte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(float value, float min, float max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Clamp(ushort value, ushort min, ushort max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint Clamp(uint value, uint min, uint max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong Clamp(ulong value, ulong min, ulong max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Floor(decimal d) + { + return decimal.Floor(d); + } + + public static double IEEERemainder(double x, double y) + { + if (double.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + var regularMod = x % y; + + if (double.IsNaN(regularMod)) + { + return double.NaN; + } + + if ((regularMod == 0) && double.IsNegative(x)) + { + return double.NegativeZero; + } + + var alternativeResult = (regularMod - (Abs(y) * Sign(x))); + + if (Abs(alternativeResult) == Abs(regularMod)) + { + var divisionResult = x / y; + var roundedResult = Round(divisionResult); + + if (Abs(roundedResult) > Abs(divisionResult)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + if (Abs(alternativeResult) < Abs(regularMod)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + public static double Log(double a, double newBase) + { + if (double.IsNaN(a)) + { + return a; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(newBase)) + { + return newBase; // IEEE 754-2008: NaN payload must be preserved + } + + if (newBase == 1) + { + return double.NaN; + } + + if ((a != 1) && ((newBase == 0) || double.IsPositiveInfinity(newBase))) + { + return double.NaN; + } + + return (Log(a) / Log(newBase)); + } + + [NonVersionable] + public static byte Max(byte val1, byte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Max(decimal val1, decimal val2) + { + return decimal.Max(val1, val2); + } + + public static double Max(double val1, double val2) + { + if (val1 > val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Max(short val1, short val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Max(int val1, int val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Max(long val1, long val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Max(sbyte val1, sbyte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + public static float Max(float val1, float val2) + { + if (val1 > val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Max(ushort val1, ushort val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Max(uint val1, uint val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Max(ulong val1, ulong val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static byte Min(byte val1, byte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Min(decimal val1, decimal val2) + { + return decimal.Min(val1, val2); + } + + public static double Min(double val1, double val2) + { + if (val1 < val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Min(short val1, short val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Min(int val1, int val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Min(long val1, long val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Min(sbyte val1, sbyte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + public static float Min(float val1, float val2) + { + if (val1 < val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Min(ushort val1, ushort val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Min(uint val1, uint val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Min(ulong val1, ulong val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d) + { + return decimal.Round(d, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals) + { + return decimal.Round(d, decimals); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, MidpointRounding mode) + { + return decimal.Round(d, 0, mode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals, MidpointRounding mode) + { + return decimal.Round(d, decimals, mode); + } + + [Intrinsic] + public static double Round(double a) + { + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + + if (a == (double)((long)a)) + { + return a; + } + + // We had a number that was equally close to 2 integers. + // We need to return the even one. + + double flrTempVal = Floor(a + 0.5); + + if ((a == (Floor(a) + 0.5)) && (fmod(flrTempVal, 2.0) != 0)) + { + flrTempVal -= 1.0; + } + + return copysign(flrTempVal, a); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, int digits) + { + return Round(value, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, MidpointRounding mode) + { + return Round(value, 0, mode); + } + + public static unsafe double Round(double value, int digits, MidpointRounding mode) + { + if ((digits < 0) || (digits > maxRoundingDigits)) + { + throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + } + + if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); + } + Contract.EndContractBlock(); + + if (Abs(value) < doubleRoundLimit) + { + var power10 = roundPower10Double[digits]; + + value *= power10; + + if (mode == MidpointRounding.AwayFromZero) + { + var fraction = modf(value, &value); + + if (Abs(fraction) >= 0.5) + { + value += Sign(fraction); + } + } + else + { + value = Round(value); + } + + value /= power10; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(decimal value) + { + return decimal.Sign(ref value); + } + + public static int Sign(double value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(short value) + { + return Sign((int)value); + } + + public static int Sign(int value) + { + return unchecked(value >> 31 | (int)((uint)-value >> 31)); + } + + public static int Sign(long value) + { + return unchecked((int)(value >> 63 | (long)((ulong)-value >> 63))); + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(sbyte value) + { + return Sign((int)value); + } + + public static int Sign(float value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Truncate(decimal d) + { + return decimal.Truncate(d); + } + + public static unsafe double Truncate(double d) + { + modf(d, &d); + return d; + } + + private static short AbsHelper(short value) + { + Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == short.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + Contract.EndContractBlock(); + + return ((short)(-value)); + } + + private static int AbsHelper(int value) + { + Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == int.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + Contract.EndContractBlock(); + + return -value; + } + + private static long AbsHelper(long value) + { + Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == long.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + Contract.EndContractBlock(); + + return -value; + } + + private static sbyte AbsHelper(sbyte value) + { + Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == sbyte.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + Contract.EndContractBlock(); + + return ((sbyte)(-value)); + } + + private static unsafe double copysign(double x, double y) + { + var xbits = BitConverter.DoubleToInt64Bits(x); + var ybits = BitConverter.DoubleToInt64Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 63) != 0) + { + return BitConverter.Int64BitsToDouble(xbits ^ long.MinValue); + } + + return x; + } + + private static void ThrowMinMaxException<T>(T min, T max) + { + throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max)); + } + } +} diff --git a/src/mscorlib/src/System/Math.cs b/src/mscorlib/src/System/Math.cs index 33d0f7701376..d5d9c3d92b58 100644 --- a/src/mscorlib/src/System/Math.cs +++ b/src/mscorlib/src/System/Math.cs @@ -13,712 +13,73 @@ //This class contains only static members and doesn't require serialization. -using System; -using System.Runtime; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - public static class Math + public static partial class Math { - private static double doubleRoundLimit = 1e16d; + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Abs(double value); - private const int maxRoundingDigits = 15; + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern float Abs(float value); - // This table is required for the Round function which can specify the number of digits to round to - private static double[] roundPower10Double = new double[] { - 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, - 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 - }; - - public const double PI = 3.14159265358979323846; - public const double E = 2.7182818284590452354; - - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Acos(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Asin(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Atan(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Atan2(double y, double x); - public static Decimal Ceiling(Decimal d) - { - return Decimal.Ceiling(d); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Ceiling(double a); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Cos(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Cosh(double value); - public static Decimal Floor(Decimal d) - { - return Decimal.Floor(d); - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Exp(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Floor(double d); - private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) - { - if (Abs(value) < doubleRoundLimit) - { - Double power10 = roundPower10Double[digits]; - value *= power10; - if (mode == MidpointRounding.AwayFromZero) - { - double fraction = SplitFractionDouble(&value); - if (Abs(fraction) >= 0.5d) - { - value += Sign(fraction); - } - } - else - { - // On X86 this can be inlined to just a few instructions - value = Round(value); - } - value /= power10; - } - return value; - } - - private unsafe static double InternalTruncate(double d) - { - SplitFractionDouble(&d); - return d; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Sin(double a); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Tan(double a); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Sinh(double value); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Tanh(double value); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Round(double a); - - public static double Round(double value, int digits) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - Contract.EndContractBlock(); - return InternalRound(value, digits, MidpointRounding.ToEven); - } - - public static double Round(double value, MidpointRounding mode) - { - return Round(value, 0, mode); - } - - public static double Round(double value, int digits, MidpointRounding mode) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); - } - Contract.EndContractBlock(); - return InternalRound(value, digits, mode); - } - - public static Decimal Round(Decimal d) - { - return Decimal.Round(d, 0); - } - - public static Decimal Round(Decimal d, int decimals) - { - return Decimal.Round(d, decimals); - } - - public static Decimal Round(Decimal d, MidpointRounding mode) - { - return Decimal.Round(d, 0, mode); - } - - public static Decimal Round(Decimal d, int decimals, MidpointRounding mode) - { - return Decimal.Round(d, decimals, mode); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static unsafe extern double SplitFractionDouble(double* value); - - public static Decimal Truncate(Decimal d) - { - return Decimal.Truncate(d); - } - - public static double Truncate(double d) - { - return InternalTruncate(d); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Sqrt(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] + [MethodImpl(MethodImplOptions.InternalCall)] public static extern double Log(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Log10(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Exp(double d); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double Pow(double x, double y); - - public static double IEEERemainder(double x, double y) - { - if (Double.IsNaN(x)) - { - return x; // IEEE 754-2008: NaN payload must be preserved - } - if (Double.IsNaN(y)) - { - return y; // IEEE 754-2008: NaN payload must be preserved - } - - double regularMod = x % y; - if (Double.IsNaN(regularMod)) - { - return Double.NaN; - } - if (regularMod == 0) - { - if (Double.IsNegative(x)) - { - return Double.NegativeZero; - } - } - double alternativeResult; - alternativeResult = regularMod - (Math.Abs(y) * Math.Sign(x)); - if (Math.Abs(alternativeResult) == Math.Abs(regularMod)) - { - double divisionResult = x / y; - double roundedResult = Math.Round(divisionResult); - if (Math.Abs(roundedResult) > Math.Abs(divisionResult)) - { - return alternativeResult; - } - else - { - return regularMod; - } - } - if (Math.Abs(alternativeResult) < Math.Abs(regularMod)) - { - return alternativeResult; - } - else - { - return regularMod; - } - } - - /*================================Abs========================================= - **Returns the absolute value of it's argument. - ============================================================================*/ - [CLSCompliant(false)] - public static sbyte Abs(sbyte value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static sbyte AbsHelper(sbyte value) - { - Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); - if (value == SByte.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return ((sbyte)(-value)); - } - - public static short Abs(short value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static short AbsHelper(short value) - { - Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); - if (value == Int16.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return (short)-value; - } - - public static int Abs(int value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static int AbsHelper(int value) - { - Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); - if (value == Int32.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return -value; - } - - public static long Abs(long value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static long AbsHelper(long value) - { - Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); - if (value == Int64.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return -value; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - extern public static float Abs(float value); - // This is special code to handle NaN (We need to make sure NaN's aren't - // negated). In CSharp, the else clause here should always be taken if - // value is NaN, since the normal case is taken if and only if value < 0. - // To illustrate this completely, a compiler has translated this into: - // "load value; load 0; bge; ret -value ; ret value". - // The bge command branches for comparisons with the unordered NaN. So - // it runs the else case, which returns +value instead of negating it. - // return (value < 0) ? -value : value; - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - extern public static double Abs(double value); - // This is special code to handle NaN (We need to make sure NaN's aren't - // negated). In CSharp, the else clause here should always be taken if - // value is NaN, since the normal case is taken if and only if value < 0. - // To illustrate this completely, a compiler has translated this into: - // "load value; load 0; bge; ret -value ; ret value". - // The bge command branches for comparisons with the unordered NaN. So - // it runs the else case, which returns +value instead of negating it. - // return (value < 0) ? -value : value; - - public static Decimal Abs(Decimal value) - { - return Decimal.Abs(value); - } - - /*================================MAX========================================= - **Returns the larger of val1 and val2 - ============================================================================*/ - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static sbyte Max(sbyte val1, sbyte val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static byte Max(byte val1, byte val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static short Max(short val1, short val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static ushort Max(ushort val1, ushort val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static int Max(int val1, int val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static uint Max(uint val1, uint val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static long Max(long val1, long val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static ulong Max(ulong val1, ulong val2) - { - return (val1 >= val2) ? val1 : val2; - } - public static float Max(float val1, float val2) - { - if (val1 > val2) - return val1; - - if (Single.IsNaN(val1)) - return val1; - - return val2; - } - - public static double Max(double val1, double val2) - { - if (val1 > val2) - return val1; - - if (Double.IsNaN(val1)) - return val1; - - return val2; - } - - public static Decimal Max(Decimal val1, Decimal val2) - { - return Decimal.Max(val1, val2); - } - - /*================================MIN========================================= - **Returns the smaller of val1 and val2. - ============================================================================*/ - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static sbyte Min(sbyte val1, sbyte val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static byte Min(byte val1, byte val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static short Min(short val1, short val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static ushort Min(ushort val1, ushort val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static int Min(int val1, int val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static uint Min(uint val1, uint val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [System.Runtime.Versioning.NonVersionable] - public static long Min(long val1, long val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [System.Runtime.Versioning.NonVersionable] - public static ulong Min(ulong val1, ulong val2) - { - return (val1 <= val2) ? val1 : val2; - } - - public static float Min(float val1, float val2) - { - if (val1 < val2) - return val1; - - if (Single.IsNaN(val1)) - return val1; - - return val2; - } - - public static double Min(double val1, double val2) - { - if (val1 < val2) - return val1; - - if (Double.IsNaN(val1)) - return val1; - - return val2; - } - - public static Decimal Min(Decimal val1, Decimal val2) - { - return Decimal.Min(val1, val2); - } - - /*=====================================Clamp==================================== - ** - ==============================================================================*/ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Byte Clamp(Byte value, Byte min, Byte max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Decimal Clamp(Decimal value, Decimal min, Decimal max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Double Clamp(Double value, Double min, Double max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Int16 Clamp(Int16 value, Int16 min, Int16 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Int32 Clamp(Int32 value, Int32 min, Int32 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Int64 Clamp(Int64 value, Int64 min, Int64 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static SByte Clamp(SByte value, SByte min, SByte max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Single Clamp(Single value, Single min, Single max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static UInt16 Clamp(UInt16 value, UInt16 min, UInt16 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static UInt32 Clamp(UInt32 value, UInt32 min, UInt32 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static UInt64 Clamp(UInt64 value, UInt64 min, UInt64 max) - { - if (min > max) - ThrowMinMaxException(min, max); - if (value < min) - return min; - else if (value > max) - return max; - return value; - } - - private static void ThrowMinMaxException<T>(T min, T max) - { - throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max)); - } - - /*=====================================Log====================================== - ** - ==============================================================================*/ - public static double Log(double a, double newBase) - { - if (Double.IsNaN(a)) - { - return a; // IEEE 754-2008: NaN payload must be preserved - } - if (Double.IsNaN(newBase)) - { - return newBase; // IEEE 754-2008: NaN payload must be preserved - } - - if (newBase == 1) - return Double.NaN; - if (a != 1 && (newBase == 0 || Double.IsPositiveInfinity(newBase))) - return Double.NaN; - - return (Log(a) / Log(newBase)); - } - - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - [CLSCompliant(false)] - public static int Sign(sbyte value) => Sign((int)value); - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - public static int Sign(short value) => Sign((int)value); - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - public static int Sign(int value) => unchecked(value >> 31 | (int)((uint)-value >> 31)); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Log10(double d); - public static int Sign(long value) => unchecked((int)(value >> 63 | (long)((ulong)-value >> 63))); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Pow(double x, double y); - public static int Sign(float value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else if (value == 0) - return 0; - throw new ArithmeticException(SR.Arithmetic_NaN); - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Sin(double a); - public static int Sign(double value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else if (value == 0) - return 0; - throw new ArithmeticException(SR.Arithmetic_NaN); - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Sinh(double value); - public static int Sign(decimal value) => decimal.Sign(ref value); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Sqrt(double d); - public static long BigMul(int a, int b) - { - return ((long)a) * b; - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Tan(double a); - public static int DivRem(int a, int b, out int result) - { - // TODO https://github.com/dotnet/coreclr/issues/3439: - // Restore to using % and / when the JIT is able to eliminate one of the idivs. - // In the meantime, a * and - is measurably faster than an extra /. - int div = a / b; - result = a - (div * b); - return div; - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern double Tanh(double value); - public static long DivRem(long a, long b, out long result) - { - // TODO https://github.com/dotnet/coreclr/issues/3439: - // Restore to using % and / when the JIT is able to eliminate one of the idivs. - // In the meantime, a * and - is measurably faster than an extra /. - long div = a / b; - result = a - (div * b); - return div; - } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern double fmod(double x, double y); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe double modf(double x, double* intptr); } } diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 7a1c5b237d80..3c8378764f63 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -652,13 +652,13 @@ FCFuncStart(gMathFuncs) FCIntrinsic("Cosh", COMDouble::Cosh, CORINFO_INTRINSIC_Cosh) FCIntrinsic("Exp", COMDouble::Exp, CORINFO_INTRINSIC_Exp) FCIntrinsic("Floor", COMDouble::Floor, CORINFO_INTRINSIC_Floor) + FCFuncElement("fmod", COMDouble::FMod) FCFuncElement("Log", COMDouble::Log) FCIntrinsic("Log10", COMDouble::Log10, CORINFO_INTRINSIC_Log10) + FCFuncElement("modf", COMDouble::ModF) FCIntrinsic("Pow", COMDouble::Pow, CORINFO_INTRINSIC_Pow) - FCIntrinsic("Round", COMDouble::Round, CORINFO_INTRINSIC_Round) FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin) FCIntrinsic("Sinh", COMDouble::Sinh, CORINFO_INTRINSIC_Sinh) - FCFuncElement("SplitFractionDouble", COMDouble::ModF) FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt) FCIntrinsic("Tan", COMDouble::Tan, CORINFO_INTRINSIC_Tan) FCIntrinsic("Tanh", COMDouble::Tanh, CORINFO_INTRINSIC_Tanh) From 687b880092a0769d0b037faf44893bdf1c05b54c Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 21:09:51 -0700 Subject: [PATCH 3/8] Updating the new 'Round' named intrinsic to map to the legacy 'Round' intrinsic. --- src/jit/compiler.h | 5 + src/jit/importer.cpp | 252 ++++++++++++++++++++++++------------------- 2 files changed, 148 insertions(+), 109 deletions(-) diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 0997154b6555..11283c6b47fb 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -3007,6 +3007,11 @@ class Compiler bool isJitIntrinsic, CorInfoIntrinsics* pIntrinsicID, bool* isSpecialIntrinsic = nullptr); + GenTree* impMathIntrinsic(CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO* sig, + var_types callType, + CorInfoIntrinsics intrinsicID, + bool tailCall); NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); GenTreePtr impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd, CORINFO_SIG_INFO* sig, diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index c6bdb0d53d51..90432159a5e3 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3346,7 +3346,6 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, case CORINFO_INTRINSIC_Sqrt: case CORINFO_INTRINSIC_Abs: case CORINFO_INTRINSIC_Cos: - case CORINFO_INTRINSIC_Round: case CORINFO_INTRINSIC_Cosh: case CORINFO_INTRINSIC_Sinh: case CORINFO_INTRINSIC_Tan: @@ -3360,97 +3359,7 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, case CORINFO_INTRINSIC_Exp: case CORINFO_INTRINSIC_Ceiling: case CORINFO_INTRINSIC_Floor: - - // These are math intrinsics - - assert(callType != TYP_STRUCT); - - op1 = nullptr; - -#if defined(LEGACY_BACKEND) - if (IsTargetIntrinsic(intrinsicID)) -#elif !defined(_TARGET_X86_) - // Intrinsics that are not implemented directly by target instructions will - // be re-materialized as users calls in rationalizer. For prefixed tail calls, - // don't do this optimization, because - // a) For back compatibility reasons on desktop.Net 4.6 / 4.6.1 - // b) It will be non-trivial task or too late to re-materialize a surviving - // tail prefixed GT_INTRINSIC as tail call in rationalizer. - if (!IsIntrinsicImplementedByUserCall(intrinsicID) || !tailCall) -#else - // On x86 RyuJIT, importing intrinsics that are implemented as user calls can cause incorrect calculation - // of the depth of the stack if these intrinsics are used as arguments to another call. This causes bad - // code generation for certain EH constructs. - if (!IsIntrinsicImplementedByUserCall(intrinsicID)) -#endif - { - switch (sig->numArgs) - { - case 1: - op1 = impPopStack().val; - -#if FEATURE_X87_DOUBLES - - // X87 stack doesn't differentiate between float/double - // so it doesn't need a cast, but everybody else does - // Just double check it is at least a FP type - noway_assert(varTypeIsFloating(op1)); - -#else // FEATURE_X87_DOUBLES - - if (op1->TypeGet() != callType) - { - op1 = gtNewCastNode(callType, op1, callType); - } - -#endif // FEATURE_X87_DOUBLES - - op1 = new (this, GT_INTRINSIC) - GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method); - break; - - case 2: - op2 = impPopStack().val; - op1 = impPopStack().val; - -#if FEATURE_X87_DOUBLES - - // X87 stack doesn't differentiate between float/double - // so it doesn't need a cast, but everybody else does - // Just double check it is at least a FP type - noway_assert(varTypeIsFloating(op2)); - noway_assert(varTypeIsFloating(op1)); - -#else // FEATURE_X87_DOUBLES - - if (op2->TypeGet() != callType) - { - op2 = gtNewCastNode(callType, op2, callType); - } - if (op1->TypeGet() != callType) - { - op1 = gtNewCastNode(callType, op1, callType); - } - -#endif // FEATURE_X87_DOUBLES - - op1 = new (this, GT_INTRINSIC) - GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicID, method); - break; - - default: - NO_WAY("Unsupported number of args for Math Instrinsic"); - } - -#ifndef LEGACY_BACKEND - if (IsIntrinsicImplementedByUserCall(intrinsicID)) - { - op1->gtFlags |= GTF_CALL; - } -#endif - } - - retNode = op1; + retNode = impMathIntrinsic(method, sig, callType, intrinsicID, tailCall); break; #ifdef _TARGET_XARCH_ @@ -3850,6 +3759,18 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, break; } + case NI_MathF_Round: + case NI_Math_Round: + { + // Math.Round and MathF.Round used to be a legacy JIT intrinsic. In order + // to simplify the transition, we will just treat it as if it was still the + // old intrinsic, CORINFO_INTRINSIC_Round. This should end up flowing properly + // everywhere else. + + retNode = impMathIntrinsic(method, sig, callType, CORINFO_INTRINSIC_Round, tailCall); + break; + } + default: break; } @@ -3873,6 +3794,123 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, return retNode; } +GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO* sig, + var_types callType, + CorInfoIntrinsics intrinsicID, + bool tailCall) +{ + GenTree* op1; + GenTree* op2; + + assert(callType != TYP_STRUCT); + assert((intrinsicID == CORINFO_INTRINSIC_Sin) + || (intrinsicID == CORINFO_INTRINSIC_Sqrt) + || (intrinsicID == CORINFO_INTRINSIC_Abs) + || (intrinsicID == CORINFO_INTRINSIC_Cos) + || (intrinsicID == CORINFO_INTRINSIC_Round) + || (intrinsicID == CORINFO_INTRINSIC_Cosh) + || (intrinsicID == CORINFO_INTRINSIC_Sinh) + || (intrinsicID == CORINFO_INTRINSIC_Tan) + || (intrinsicID == CORINFO_INTRINSIC_Tanh) + || (intrinsicID == CORINFO_INTRINSIC_Asin) + || (intrinsicID == CORINFO_INTRINSIC_Acos) + || (intrinsicID == CORINFO_INTRINSIC_Atan) + || (intrinsicID == CORINFO_INTRINSIC_Atan2) + || (intrinsicID == CORINFO_INTRINSIC_Log10) + || (intrinsicID == CORINFO_INTRINSIC_Pow) + || (intrinsicID == CORINFO_INTRINSIC_Exp) + || (intrinsicID == CORINFO_INTRINSIC_Ceiling) + || (intrinsicID == CORINFO_INTRINSIC_Floor)); + + op1 = nullptr; + +#if defined(LEGACY_BACKEND) + if (IsTargetIntrinsic(intrinsicID)) +#elif !defined(_TARGET_X86_) + // Intrinsics that are not implemented directly by target instructions will + // be re-materialized as users calls in rationalizer. For prefixed tail calls, + // don't do this optimization, because + // a) For back compatibility reasons on desktop.Net 4.6 / 4.6.1 + // b) It will be non-trivial task or too late to re-materialize a surviving + // tail prefixed GT_INTRINSIC as tail call in rationalizer. + if (!IsIntrinsicImplementedByUserCall(intrinsicID) || !tailCall) +#else + // On x86 RyuJIT, importing intrinsics that are implemented as user calls can cause incorrect calculation + // of the depth of the stack if these intrinsics are used as arguments to another call. This causes bad + // code generation for certain EH constructs. + if (!IsIntrinsicImplementedByUserCall(intrinsicID)) +#endif + { + switch (sig->numArgs) + { + case 1: + op1 = impPopStack().val; + +#if FEATURE_X87_DOUBLES + + // X87 stack doesn't differentiate between float/double + // so it doesn't need a cast, but everybody else does + // Just double check it is at least a FP type + noway_assert(varTypeIsFloating(op1)); + +#else // FEATURE_X87_DOUBLES + + if (op1->TypeGet() != callType) + { + op1 = gtNewCastNode(callType, op1, callType); + } + +#endif // FEATURE_X87_DOUBLES + + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method); + break; + + case 2: + op2 = impPopStack().val; + op1 = impPopStack().val; + +#if FEATURE_X87_DOUBLES + + // X87 stack doesn't differentiate between float/double + // so it doesn't need a cast, but everybody else does + // Just double check it is at least a FP type + noway_assert(varTypeIsFloating(op2)); + noway_assert(varTypeIsFloating(op1)); + +#else // FEATURE_X87_DOUBLES + + if (op2->TypeGet() != callType) + { + op2 = gtNewCastNode(callType, op2, callType); + } + if (op1->TypeGet() != callType) + { + op1 = gtNewCastNode(callType, op1, callType); + } + +#endif // FEATURE_X87_DOUBLES + + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicID, method); + break; + + default: + NO_WAY("Unsupported number of args for Math Instrinsic"); + } + +#ifndef LEGACY_BACKEND + if (IsIntrinsicImplementedByUserCall(intrinsicID)) + { + op1->gtFlags |= GTF_CALL; + } +#endif + } + + return op1; +} + //------------------------------------------------------------------------ // lookupNamedIntrinsic: map method to jit named intrinsic value // @@ -3895,30 +3933,26 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) const char* namespaceName = nullptr; const char* methodName = info.compCompHnd->getMethodNameFromMetadata(method, &className, &namespaceName); - if ((namespaceName != nullptr) && strcmp(namespaceName, "System") == 0) + // Currently we only have intrinsics at the method level, so we can check that + // namespaceName, className, and methodName are all not null upfront. If this ever + // changes (such as if we support all methods in a class as intrinsic), we can reorder + // the checks to account for that. + + if ((namespaceName != nullptr) && (className != nullptr) && (methodName != nullptr)) { - if (className != nullptr) + if (strcmp(namespaceName, "System") == 0) { - if (strcmp(className, "Enum") == 0) + if ((strcmp(className, "Enum") == 0) && (strcmp(methodName, "HasFlag") == 0)) { - if ((methodName != nullptr) && strcmp(methodName, "HasFlag") == 0) - { - result = NI_Enum_HasFlag; - } + result = NI_Enum_HasFlag; } - else if (strcmp(className, "MathF") == 0) + else if ((strcmp(className, "MathF") == 0) && (strcmp(methodName, "Round") == 0)) { - if ((methodName != nullptr) && strcmp(methodName, "Round") == 0) - { - result = NI_MathF_Round; - } + result = NI_MathF_Round; } - else if (strcmp(clasName, "Math") == 0) + else if ((strcmp(className, "Math") == 0) && (strcmp(methodName, "Round") == 0)) { - if ((methodName != nullptr) && strcmp(methodName, "Round") == 0) - { - result = NI_Math_Round; - } + result = NI_Math_Round; } } } From 04fdd3db68b5c6681a208b60d7c305fb436c0443 Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 21:26:22 -0700 Subject: [PATCH 4/8] Adding a message to the Math.Round and MathF.Round implementations --- src/mscorlib/shared/System/Math.cs | 5 +++++ src/mscorlib/shared/System/MathF.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/mscorlib/shared/System/Math.cs b/src/mscorlib/shared/System/Math.cs index 8c15715a8693..6cfca945ae3d 100644 --- a/src/mscorlib/shared/System/Math.cs +++ b/src/mscorlib/shared/System/Math.cs @@ -605,6 +605,11 @@ public static decimal Round(decimal d, int decimals, MidpointRounding mode) [Intrinsic] public static double Round(double a) { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + // If the number has no fractional part do nothing // This shortcut is necessary to workaround precision loss in borderline cases on some platforms diff --git a/src/mscorlib/shared/System/MathF.cs b/src/mscorlib/shared/System/MathF.cs index 19089dd220a9..f596c5a172a1 100644 --- a/src/mscorlib/shared/System/MathF.cs +++ b/src/mscorlib/shared/System/MathF.cs @@ -128,6 +128,11 @@ public static float Min(float x, float y) [Intrinsic] public static float Round(float x) { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + // If the number has no fractional part do nothing // This shortcut is necessary to workaround precision loss in borderline cases on some platforms From 76f2ba186e14a35f845fb5e3101e63644a39a3f0 Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 21:31:30 -0700 Subject: [PATCH 5/8] Applying the code format patch for importer.cpp --- src/jit/importer.cpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 90432159a5e3..a75535524cf9 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3804,24 +3804,15 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, GenTree* op2; assert(callType != TYP_STRUCT); - assert((intrinsicID == CORINFO_INTRINSIC_Sin) - || (intrinsicID == CORINFO_INTRINSIC_Sqrt) - || (intrinsicID == CORINFO_INTRINSIC_Abs) - || (intrinsicID == CORINFO_INTRINSIC_Cos) - || (intrinsicID == CORINFO_INTRINSIC_Round) - || (intrinsicID == CORINFO_INTRINSIC_Cosh) - || (intrinsicID == CORINFO_INTRINSIC_Sinh) - || (intrinsicID == CORINFO_INTRINSIC_Tan) - || (intrinsicID == CORINFO_INTRINSIC_Tanh) - || (intrinsicID == CORINFO_INTRINSIC_Asin) - || (intrinsicID == CORINFO_INTRINSIC_Acos) - || (intrinsicID == CORINFO_INTRINSIC_Atan) - || (intrinsicID == CORINFO_INTRINSIC_Atan2) - || (intrinsicID == CORINFO_INTRINSIC_Log10) - || (intrinsicID == CORINFO_INTRINSIC_Pow) - || (intrinsicID == CORINFO_INTRINSIC_Exp) - || (intrinsicID == CORINFO_INTRINSIC_Ceiling) - || (intrinsicID == CORINFO_INTRINSIC_Floor)); + assert((intrinsicID == CORINFO_INTRINSIC_Sin) || (intrinsicID == CORINFO_INTRINSIC_Sqrt) || + (intrinsicID == CORINFO_INTRINSIC_Abs) || (intrinsicID == CORINFO_INTRINSIC_Cos) || + (intrinsicID == CORINFO_INTRINSIC_Round) || (intrinsicID == CORINFO_INTRINSIC_Cosh) || + (intrinsicID == CORINFO_INTRINSIC_Sinh) || (intrinsicID == CORINFO_INTRINSIC_Tan) || + (intrinsicID == CORINFO_INTRINSIC_Tanh) || (intrinsicID == CORINFO_INTRINSIC_Asin) || + (intrinsicID == CORINFO_INTRINSIC_Acos) || (intrinsicID == CORINFO_INTRINSIC_Atan) || + (intrinsicID == CORINFO_INTRINSIC_Atan2) || (intrinsicID == CORINFO_INTRINSIC_Log10) || + (intrinsicID == CORINFO_INTRINSIC_Pow) || (intrinsicID == CORINFO_INTRINSIC_Exp) || + (intrinsicID == CORINFO_INTRINSIC_Ceiling) || (intrinsicID == CORINFO_INTRINSIC_Floor)); op1 = nullptr; @@ -3863,8 +3854,7 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, #endif // FEATURE_X87_DOUBLES - op1 = new (this, GT_INTRINSIC) - GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method); + op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method); break; case 2: @@ -3892,8 +3882,7 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, #endif // FEATURE_X87_DOUBLES - op1 = new (this, GT_INTRINSIC) - GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicID, method); + op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicID, method); break; default: From ba7ddb9294aac6df28e208e15bcf84448b915d17 Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 22:07:00 -0700 Subject: [PATCH 6/8] Fixing up some comments in importer.cpp --- src/jit/importer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index a75535524cf9..a101295ccd38 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3762,7 +3762,7 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, case NI_MathF_Round: case NI_Math_Round: { - // Math.Round and MathF.Round used to be a legacy JIT intrinsic. In order + // Math.Round and MathF.Round used to be a traditional JIT intrinsic. In order // to simplify the transition, we will just treat it as if it was still the // old intrinsic, CORINFO_INTRINSIC_Round. This should end up flowing properly // everywhere else. @@ -3923,9 +3923,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) const char* methodName = info.compCompHnd->getMethodNameFromMetadata(method, &className, &namespaceName); // Currently we only have intrinsics at the method level, so we can check that - // namespaceName, className, and methodName are all not null upfront. If this ever - // changes (such as if we support all methods in a class as intrinsic), we can reorder - // the checks to account for that. + // namespaceName, className, and methodName are all not null upfront. if ((namespaceName != nullptr) && (className != nullptr) && (methodName != nullptr)) { From 86e89137c9f82f692de71272f649f396fe745169 Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Thu, 21 Sep 2017 22:09:01 -0700 Subject: [PATCH 7/8] Adding back the case statement for CORINFO_INTRINSIC_Round, since it is required for Desktop compat. --- src/jit/importer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index a101295ccd38..30969a134931 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3346,6 +3346,7 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, case CORINFO_INTRINSIC_Sqrt: case CORINFO_INTRINSIC_Abs: case CORINFO_INTRINSIC_Cos: + case CORINFO_INTRINSIC_Round: case CORINFO_INTRINSIC_Cosh: case CORINFO_INTRINSIC_Sinh: case CORINFO_INTRINSIC_Tan: From eebceeb68d55590eab46c422a45d3f55edb2a123 Mon Sep 17 00:00:00 2001 From: Tanner Gooding <tagoo@outlook.com> Date: Fri, 22 Sep 2017 17:52:16 -0700 Subject: [PATCH 8/8] Renaming the non-shared copy of Math.cs to Math.CoreCLR.cs --- src/mscorlib/System.Private.CoreLib.csproj | 2 +- src/mscorlib/src/System/{Math.cs => Math.CoreCLR.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/mscorlib/src/System/{Math.cs => Math.CoreCLR.cs} (100%) diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj index 90294fd527a7..557d744616da 100644 --- a/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/mscorlib/System.Private.CoreLib.csproj @@ -329,7 +329,7 @@ <Compile Include="$(BclSourcesRoot)\System\InsufficientMemoryException.cs" /> <Compile Include="$(BclSourcesRoot)\System\IntPtr.cs" /> <Compile Include="$(BclSourcesRoot)\System\Internal.cs" /> - <Compile Include="$(BclSourcesRoot)\System\Math.cs" /> + <Compile Include="$(BclSourcesRoot)\System\Math.CoreCLR.cs" /> <Compile Include="$(BclSourcesRoot)\System\MathF.CoreCLR.cs" /> <Compile Include="$(BclSourcesRoot)\System\mda.cs" /> <Compile Include="$(BclSourcesRoot)\System\MissingFieldException.cs" /> diff --git a/src/mscorlib/src/System/Math.cs b/src/mscorlib/src/System/Math.CoreCLR.cs similarity index 100% rename from src/mscorlib/src/System/Math.cs rename to src/mscorlib/src/System/Math.CoreCLR.cs