From b2d559e7d6c6dcf3df660e91199f4a230b61ead1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 12:05:59 -0700 Subject: [PATCH 01/16] Change the ReciprocalEstimate and ReciprocalSqrtEstimate APIs to be mustExpand on RyuJIT --- src/coreclr/jit/importercalls.cpp | 45 ++++++++++++------- .../System.Private.CoreLib/src/System/Math.cs | 10 ++++- .../src/System/MathF.cs | 10 ++++- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 943c75d2639bf..8053e7489d340 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3125,15 +3125,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // To be fixed in https://github.com/dotnet/runtime/pull/77465 const bool tier0opts = !opts.compDbgCode && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_MIN_OPT); - if (tier0opts) - { - // The *Estimate APIs are allowed to differ in behavior across hardware - // so ensure we treat them as "betterToExpand" to get deterministic behavior - - betterToExpand |= (ni == NI_System_Math_ReciprocalEstimate); - betterToExpand |= (ni == NI_System_Math_ReciprocalSqrtEstimate); - } - else if (!mustExpand) + if (!mustExpand) { switch (ni) { @@ -7471,6 +7463,8 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) { case NI_System_Math_Abs: case NI_System_Math_Sqrt: + case NI_System_Math_ReciprocalEstimate: + case NI_System_Math_ReciprocalSqrtEstimate: return true; default: @@ -8847,7 +8841,7 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, simdSize = 16; } - GenTree* op1 = impPopStack().val; + GenTree* op1 = impImplicitR4orR8Cast(impPopStack().val, callType); op1 = gtNewSimdCreateScalarUnsafeNode(simdType, op1, callJitType, simdSize); op1 = gtNewSimdHWIntrinsicNode(simdType, op1, intrinsicId, callJitType, simdSize); @@ -8856,10 +8850,27 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, } #endif // FEATURE_HW_INTRINSICS - // TODO-CQ: Returning this as an intrinsic blocks inlining and is undesirable - // return impMathIntrinsic(method, sig, callType, intrinsicName, tailCall); + switch (intrinsicName) + { + case NI_System_Math_ReciprocalEstimate: + case NI_System_Math_ReciprocalSqrtEstimate: + { + GenTree* op1 = impImplicitR4orR8Cast(impPopStack().val, callType); - return nullptr; + if (intrinsicName == NI_System_Math_ReciprocalSqrtEstimate) + { + op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, + nullptr); + } + + return gtNewOperNode(GT_DIV, genActualType(callType), gtNewDconNode(1.0, callType), op1); + } + + default: + { + unreached(); + } + } } GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, @@ -8958,8 +8969,8 @@ GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, GenTreeDblCon* cnsNode = nullptr; GenTree* otherNode = nullptr; - GenTree* op2 = impStackTop().val; - GenTree* op1 = impStackTop(1).val; + GenTree* op2 = impImplicitR4orR8Cast(impStackTop().val, callType); + GenTree* op1 = impImplicitR4orR8Cast(impStackTop(1).val, callType); if (op2->IsCnsFltOrDbl()) { @@ -9227,7 +9238,7 @@ GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, retNode->AsHWIntrinsic()->Op(2) = op1; } - return gtNewSimdToScalarNode(callType, retNode, callJitType, 16); + return gtNewSimdToScalarNode(genActualType(callType), retNode, callJitType, 16); } } #endif // FEATURE_HW_INTRINSICS && TARGET_XARCH @@ -9392,7 +9403,7 @@ GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, callJitType, 16); } - return gtNewSimdToScalarNode(callType, tmp, callJitType, 16); + return gtNewSimdToScalarNode(genActualType(callType), tmp, callJitType, 16); } #endif // FEATURE_HW_INTRINSICS && TARGET_XARCH diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 521abb22465e7..5b26cd7098158 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1196,10 +1196,13 @@ public static double MinMagnitude(double x, double y) /// On hardware without specialized support, this may just return 1.0 / d. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ReciprocalEstimate(double d) { +#if MONO return 1.0 / d; +#else + return ReciprocalEstimate(d); +#endif } /// Returns an estimate of the reciprocal square root of a specified number. @@ -1210,10 +1213,13 @@ public static double ReciprocalEstimate(double d) /// On hardware without specialized support, this may just return 1.0 / Sqrt(d). /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ReciprocalSqrtEstimate(double d) { +#if MONO return 1.0 / Sqrt(d); +#else + return ReciprocalSqrtEstimate(d); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 05b404abccc51..7ca477044d2cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -314,10 +314,13 @@ public static float MinMagnitude(float x, float y) /// On hardware without specialized support, this may just return 1.0 / x. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ReciprocalEstimate(float x) { +#if MONO return 1.0f / x; +#else + return ReciprocalEstimate(x); +#endif } /// Returns an estimate of the reciprocal square root of a specified number. @@ -329,10 +332,13 @@ public static float ReciprocalEstimate(float x) /// On hardware without specialized support, this may just return 1.0 / Sqrt(x). /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ReciprocalSqrtEstimate(float x) { +#if MONO return 1.0f / Sqrt(x); +#else + return ReciprocalSqrtEstimate(x); +#endif } [Intrinsic] From 30dc925c6d2aa800583d4a6dbf19e9f30547dee1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 13:28:03 -0700 Subject: [PATCH 02/16] Apply formatting patch --- src/coreclr/jit/importercalls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 8053e7489d340..3947a23e31833 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -8859,8 +8859,8 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, if (intrinsicName == NI_System_Math_ReciprocalSqrtEstimate) { - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, - nullptr); + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, nullptr); } return gtNewOperNode(GT_DIV, genActualType(callType), gtNewDconNode(1.0, callType), op1); From 67a391b18389a23b42e941c83a2e792c419ba77c Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 14:29:20 -0700 Subject: [PATCH 03/16] Fix the RV64 and LA64 builds --- .../System.Private.CoreLib/src/System/Math.cs | 20 +++++++------------ .../src/System/MathF.cs | 20 +++++++------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 5b26cd7098158..51a7fbfc0256c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1195,15 +1195,12 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / d. /// - [Intrinsic] - public static double ReciprocalEstimate(double d) - { #if MONO - return 1.0 / d; + public static double ReciprocalEstimate(double d) => 1.0 / d; #else - return ReciprocalEstimate(d); + [Intrinsic] + public static double ReciprocalEstimate(double d) => ReciprocalEstimate(d); #endif - } /// Returns an estimate of the reciprocal square root of a specified number. /// The number whose reciprocal square root is to be estimated. @@ -1212,15 +1209,12 @@ public static double ReciprocalEstimate(double d) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(d). /// - [Intrinsic] - public static double ReciprocalSqrtEstimate(double d) - { -#if MONO - return 1.0 / Sqrt(d); +#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 + public static double ReciprocalSqrtEstimate(double d) => 1.0 / Sqrt(d); #else - return ReciprocalSqrtEstimate(d); + [Intrinsic] + public static double ReciprocalSqrtEstimate(double d) => ReciprocalSqrtEstimate(d); #endif - } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal Round(decimal d) diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 7ca477044d2cf..f43c1d70446a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -313,15 +313,12 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / x. /// - [Intrinsic] - public static float ReciprocalEstimate(float x) - { #if MONO - return 1.0f / x; + public static float ReciprocalEstimate(float x) => 1.0f / x; #else - return ReciprocalEstimate(x); + [Intrinsic] + public static float ReciprocalEstimate(float x) => ReciprocalEstimate(x); #endif - } /// Returns an estimate of the reciprocal square root of a specified number. /// The number whose reciprocal square root is to be estimated. @@ -331,15 +328,12 @@ public static float ReciprocalEstimate(float x) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(x). /// - [Intrinsic] - public static float ReciprocalSqrtEstimate(float x) - { -#if MONO - return 1.0f / Sqrt(x); +#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 + public static float ReciprocalSqrtEstimate(float x) => 1.0f / Sqrt(x); #else - return ReciprocalSqrtEstimate(x); + [Intrinsic] + public static float ReciprocalSqrtEstimate(float x) => ReciprocalSqrtEstimate(x); #endif - } [Intrinsic] public static float Round(float x) From 42a1dd599ac1ff1e2db2de13ae01cfb8f710a6a6 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 19:23:44 -0700 Subject: [PATCH 04/16] Mark the ReciprocalEstimate and ReciprocalSqrtEstimate methods as AggressiveOptimization to bypass R2R --- src/libraries/System.Private.CoreLib/src/System/Double.cs | 2 ++ src/libraries/System.Private.CoreLib/src/System/Math.cs | 2 ++ src/libraries/System.Private.CoreLib/src/System/MathF.cs | 2 ++ src/libraries/System.Private.CoreLib/src/System/Single.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 81a6792b599cd..171552afe5446 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -866,10 +866,12 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati /// [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static double ReciprocalEstimate(double x) => Math.ReciprocalEstimate(x); /// [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static double ReciprocalSqrtEstimate(double x) => Math.ReciprocalSqrtEstimate(x); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 51a7fbfc0256c..03a76af4de42d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1195,6 +1195,7 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / d. /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] #if MONO public static double ReciprocalEstimate(double d) => 1.0 / d; #else @@ -1209,6 +1210,7 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(d). /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 public static double ReciprocalSqrtEstimate(double d) => 1.0 / Sqrt(d); #else diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index f43c1d70446a3..7a05c3bb2a825 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -313,6 +313,7 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / x. /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] #if MONO public static float ReciprocalEstimate(float x) => 1.0f / x; #else @@ -328,6 +329,7 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(x). /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 public static float ReciprocalSqrtEstimate(float x) => 1.0f / Sqrt(x); #else diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index fe96bb1b419f7..cfcaec9a6fb95 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -849,10 +849,12 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio /// [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static float ReciprocalEstimate(float x) => MathF.ReciprocalEstimate(x); /// [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static float ReciprocalSqrtEstimate(float x) => MathF.ReciprocalSqrtEstimate(x); /// From f177bd2f643ba10142f5956efd36569b3110573e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 20:45:21 -0700 Subject: [PATCH 05/16] Mark other usages of ReciprocalEstimate and ReciprocalSqrtEstimate in Corelib with AggressiveOptimization --- src/libraries/System.Private.CoreLib/src/System/Half.cs | 2 ++ .../src/System/Runtime/InteropServices/NFloat.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index d4459c5162366..da9688ea5ab02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -1578,9 +1578,11 @@ public static int ILogB(Half x) public static Half Lerp(Half value1, Half value2, Half amount) => (Half)float.Lerp((float)value1, (float)value2, (float)amount); /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static Half ReciprocalEstimate(Half x) => (Half)MathF.ReciprocalEstimate((float)x); /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static Half ReciprocalSqrtEstimate(Half x) => (Half)MathF.ReciprocalSqrtEstimate((float)x); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index b314daec02a93..5af3948a88a6a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -1190,9 +1190,11 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati public static NFloat Lerp(NFloat value1, NFloat value2, NFloat amount) => new NFloat(NativeType.Lerp(value1._value, value2._value, amount._value)); /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static NFloat ReciprocalEstimate(NFloat x) => new NFloat(NativeType.ReciprocalEstimate(x._value)); /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static NFloat ReciprocalSqrtEstimate(NFloat x) => new NFloat(NativeType.ReciprocalSqrtEstimate(x._value)); /// From 62933bce39d7a7966d57811476e06a5df7ec9ccd Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 10 May 2024 23:52:31 -0700 Subject: [PATCH 06/16] Mark several non-deterministic APIs as BypassReadyToRun and skip intrinsic expansion in R2R --- src/coreclr/jit/hwintrinsicarm64.cpp | 56 ++++++++++++++++--- src/coreclr/jit/hwintrinsicxarch.cpp | 29 ++++++++++ src/coreclr/jit/importercalls.cpp | 22 +++++++- src/coreclr/jit/simdashwintrinsic.cpp | 34 ++++++++++- .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/Double.cs | 4 +- .../System.Private.CoreLib/src/System/Half.cs | 2 - .../System.Private.CoreLib/src/System/Math.cs | 5 +- .../src/System/MathF.cs | 5 +- .../src/System/Numerics/Vector.cs | 5 ++ .../Runtime/BypassReadyToRunAttribute.cs | 16 ++++++ .../System/Runtime/InteropServices/NFloat.cs | 2 - .../System/Runtime/Intrinsics/Vector128.cs | 5 ++ .../System/Runtime/Intrinsics/Vector256.cs | 5 ++ .../System/Runtime/Intrinsics/Vector512.cs | 5 ++ .../src/System/Runtime/Intrinsics/Vector64.cs | 5 ++ .../src/System/Single.cs | 4 +- 17 files changed, 179 insertions(+), 26 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 90dde12bde822..37d129c37aa0e 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -624,10 +624,20 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToInt32: case NI_Vector64_ConvertToInt32Native: - case NI_Vector128_ConvertToInt32: case NI_Vector128_ConvertToInt32Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToInt32: + case NI_Vector128_ConvertToInt32: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); @@ -637,10 +647,20 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToInt64: case NI_Vector64_ConvertToInt64Native: - case NI_Vector128_ConvertToInt64: case NI_Vector128_ConvertToInt64Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToInt64: + case NI_Vector128_ConvertToInt64: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); @@ -661,10 +681,20 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToUInt32: case NI_Vector64_ConvertToUInt32Native: - case NI_Vector128_ConvertToUInt32: case NI_Vector128_ConvertToUInt32Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToUInt32: + case NI_Vector128_ConvertToUInt32: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); @@ -674,10 +704,20 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToUInt64: case NI_Vector64_ConvertToUInt64Native: - case NI_Vector128_ConvertToUInt64: case NI_Vector128_ConvertToUInt64Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToUInt64: + case NI_Vector128_ConvertToUInt64: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 19062dde2d7b7..80d83e89bb6ae 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -1436,6 +1436,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNativeNode(retType, op1, CORINFO_TYPE_INT, simdBaseJitType, simdSize); break; @@ -1463,6 +1470,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1542,6 +1556,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1556,6 +1577,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1571,6 +1593,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 3947a23e31833..c6c2bc5f94b5d 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -5191,8 +5191,18 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, switch (intrinsic) { - case NI_PRIMITIVE_ConvertToInteger: case NI_PRIMITIVE_ConvertToIntegerNative: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + return nullptr; + } + FALLTHROUGH; + } + + case NI_PRIMITIVE_ConvertToInteger: { assert(sig->sigInst.methInstCount == 1); assert(varTypeIsFloating(baseType)); @@ -8765,10 +8775,16 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, assert(varTypeIsFloating(callType)); assert(sig->numArgs == 1); + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + return nullptr; + } + #if defined(FEATURE_HW_INTRINSICS) // We use compExactlyDependsOn since these are estimate APIs where - // the behavior is explicitly allowed to differ across machines and - // we want to ensure that it gets marked as such in R2R. + // the behavior is explicitly allowed to differ across machines var_types simdType = TYP_UNKNOWN; NamedIntrinsic intrinsicId = NI_Illegal; diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index c9b227440d4e5..d55a315e5d903 100644 --- a/src/coreclr/jit/simdashwintrinsic.cpp +++ b/src/coreclr/jit/simdashwintrinsic.cpp @@ -512,8 +512,39 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, switch (intrinsic) { + case NI_VectorT_ConvertToInt32Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + return nullptr; + } + break; + } + + case NI_VectorT_ConvertToInt64Native: + case NI_VectorT_ConvertToUInt32Native: + case NI_VectorT_ConvertToUInt64Native: + { + if (opts.IsReadyToRun()) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + return nullptr; + } + #if defined(TARGET_XARCH) + if (!IsBaselineVector512IsaSupportedOpportunistically()) + { + return nullptr; + } +#endif // TARGET_XARCH + break; + } + +#if defined(TARGET_XARCH) case NI_VectorT_ConvertToDouble: { if (IsBaselineVector512IsaSupportedOpportunistically()) @@ -533,11 +564,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, } case NI_VectorT_ConvertToInt64: - case NI_VectorT_ConvertToInt64Native: case NI_VectorT_ConvertToUInt32: - case NI_VectorT_ConvertToUInt32Native: case NI_VectorT_ConvertToUInt64: - case NI_VectorT_ConvertToUInt64Native: { if (IsBaselineVector512IsaSupportedOpportunistically()) { diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index ce22178ca8a0f..c2516a878ba98 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -800,6 +800,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 171552afe5446..c4f105792db51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -662,6 +663,7 @@ public static TInteger ConvertToInteger(double value) /// [Intrinsic] + [BypassReadyToRun] public static TInteger ConvertToIntegerNative(double value) where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); @@ -866,12 +868,10 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static double ReciprocalEstimate(double x) => Math.ReciprocalEstimate(x); /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static double ReciprocalSqrtEstimate(double x) => Math.ReciprocalSqrtEstimate(x); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index da9688ea5ab02..d4459c5162366 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -1578,11 +1578,9 @@ public static int ILogB(Half x) public static Half Lerp(Half value1, Half value2, Half amount) => (Half)float.Lerp((float)value1, (float)value2, (float)amount); /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static Half ReciprocalEstimate(Half x) => (Half)MathF.ReciprocalEstimate((float)x); /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static Half ReciprocalSqrtEstimate(Half x) => (Half)MathF.ReciprocalSqrtEstimate((float)x); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 03a76af4de42d..fe213516c68ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; @@ -1195,7 +1196,7 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / d. /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] + [BypassReadyToRun] #if MONO public static double ReciprocalEstimate(double d) => 1.0 / d; #else @@ -1210,7 +1211,7 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(d). /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] + [BypassReadyToRun] #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 public static double ReciprocalSqrtEstimate(double d) => 1.0 / Sqrt(d); #else diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 7a05c3bb2a825..6bda367b8201c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -313,7 +314,7 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / x. /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] + [BypassReadyToRun] #if MONO public static float ReciprocalEstimate(float x) => 1.0f / x; #else @@ -329,7 +330,7 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(x). /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] + [BypassReadyToRun] #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 public static float ReciprocalSqrtEstimate(float x) => 1.0f / Sqrt(x); #else diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index 0e968e6fca8f3..bb5811457d722 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -346,6 +347,7 @@ public static Vector ConvertToInt32(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] public static Vector ConvertToInt32Native(Vector value) => ConvertToInt32(value); /// Converts a to a using saturation on overflow. @@ -369,6 +371,7 @@ public static Vector ConvertToInt64(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] public static Vector ConvertToInt64Native(Vector value) => ConvertToInt64(value); /// Converts a to a . @@ -430,6 +433,7 @@ public static Vector ConvertToUInt32(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] public static Vector ConvertToUInt32Native(Vector value) => ConvertToUInt32(value); @@ -455,6 +459,7 @@ public static Vector ConvertToUInt64(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] public static Vector ConvertToUInt64Native(Vector value) => ConvertToUInt64(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs new file mode 100644 index 0000000000000..e9fd9f29a1cc2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime +{ + // Use this attribute to indicate that a function should never be compiled into a Ready2Run binary + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] +#if MONO + [Conditional("unnecessary")] // Mono doesn't use Ready2Run so we can remove this attribute to reduce size +#endif + internal sealed class BypassReadyToRunAttribute : Attribute + { + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index 5af3948a88a6a..b314daec02a93 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -1190,11 +1190,9 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati public static NFloat Lerp(NFloat value1, NFloat value2, NFloat amount) => new NFloat(NativeType.Lerp(value1._value, value2._value, amount._value)); /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static NFloat ReciprocalEstimate(NFloat x) => new NFloat(NativeType.ReciprocalEstimate(x._value)); /// - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static NFloat ReciprocalSqrtEstimate(NFloat x) => new NFloat(NativeType.ReciprocalSqrtEstimate(x._value)); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 4080b06c92b77..6e33be64ea8de 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.Arm; @@ -485,6 +486,7 @@ public static unsafe Vector128 ConvertToInt32(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToInt32Native(Vector128 vector) { @@ -511,6 +513,7 @@ public static unsafe Vector128 ConvertToInt64(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToInt64Native(Vector128 vector) { @@ -608,6 +611,7 @@ public static unsafe Vector128 ConvertToUInt32(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToUInt32Native(Vector128 vector) @@ -636,6 +640,7 @@ public static unsafe Vector128 ConvertToUInt64(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToUInt64Native(Vector128 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index 4d174eef19754..34be4be01f9d4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; @@ -403,6 +404,7 @@ public static Vector256 ConvertToInt32(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToInt32Native(Vector256 vector) { @@ -429,6 +431,7 @@ public static Vector256 ConvertToInt64(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToInt64Native(Vector256 vector) { @@ -516,6 +519,7 @@ public static Vector256 ConvertToUInt32(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToUInt32Native(Vector256 vector) @@ -544,6 +548,7 @@ public static Vector256 ConvertToUInt64(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToUInt64Native(Vector256 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index d63f12afb64d9..7971e42dca645 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; @@ -365,6 +366,7 @@ public static Vector512 ConvertToInt32(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToInt32Native(Vector512 vector) { @@ -391,6 +393,7 @@ public static Vector512 ConvertToInt64(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToInt64Native(Vector512 vector) { @@ -445,6 +448,7 @@ public static Vector512 ConvertToUInt32(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToUInt32Native(Vector512 vector) @@ -473,6 +477,7 @@ public static Vector512 ConvertToUInt64(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToUInt64Native(Vector512 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 04c5e0672a9d1..9292f3b17a10a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.Arm; @@ -361,6 +362,7 @@ public static unsafe Vector64 ConvertToInt32(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 ConvertToInt32Native(Vector64 vector) => ConvertToInt32(vector); @@ -386,6 +388,7 @@ public static unsafe Vector64 ConvertToInt64(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 ConvertToInt64Native(Vector64 vector) => ConvertToInt64(vector); @@ -449,6 +452,7 @@ public static unsafe Vector64 ConvertToUInt32(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 ConvertToUInt32Native(Vector64 vector) => ConvertToUInt32(vector); @@ -476,6 +480,7 @@ public static unsafe Vector64 ConvertToUInt64(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] + [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 ConvertToUInt64Native(Vector64 vector) => ConvertToUInt64(vector); diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index cfcaec9a6fb95..52ce286c58da2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -657,6 +658,7 @@ public static TInteger ConvertToInteger(float value) /// [Intrinsic] + [BypassReadyToRun] public static TInteger ConvertToIntegerNative(float value) where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); @@ -849,12 +851,10 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static float ReciprocalEstimate(float x) => MathF.ReciprocalEstimate(x); /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static float ReciprocalSqrtEstimate(float x) => MathF.ReciprocalSqrtEstimate(x); /// From afdcb1bc950d3dfca6aefa2ae898fd07886e2dbf Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 11 May 2024 09:43:53 -0700 Subject: [PATCH 07/16] Cleanup based on PR recommendations to rely on the runtime rather than attributation of non-deterministic intrinsics --- src/coreclr/jit/compiler.h | 30 +++++++-- src/coreclr/jit/hwintrinsic.cpp | 2 +- src/coreclr/jit/hwintrinsicarm64.cpp | 20 ++---- src/coreclr/jit/hwintrinsicxarch.cpp | 37 +++++------ src/coreclr/jit/importercalls.cpp | 29 ++++----- src/coreclr/jit/simdashwintrinsic.cpp | 18 ++--- .../System.Private.CoreLib.Shared.projitems | 1 - .../src/System/Double.cs | 16 ++++- .../System.Private.CoreLib/src/System/Math.cs | 21 +++--- .../src/System/MathF.cs | 21 +++--- .../src/System/Numerics/Vector.cs | 65 +++++++++++++++---- .../Runtime/BypassReadyToRunAttribute.cs | 16 ----- .../System/Runtime/Intrinsics/Vector128.cs | 5 -- .../System/Runtime/Intrinsics/Vector256.cs | 5 -- .../System/Runtime/Intrinsics/Vector512.cs | 5 -- .../src/System/Runtime/Intrinsics/Vector64.cs | 65 +++++++++++++++---- .../src/System/Single.cs | 16 ++++- 17 files changed, 223 insertions(+), 149 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 88b63c49ff8a4..4b7a3c7402d59 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4530,7 +4530,7 @@ class Compiler CORINFO_SIG_INFO* sig, CorInfoType callJitType, NamedIntrinsic intrinsicName, - bool tailCall); + bool mustExpand); GenTree* impMathIntrinsic(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, var_types callType, @@ -4561,7 +4561,8 @@ class Compiler GenTree* impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig); + CORINFO_SIG_INFO* sig, + bool mustExpand); #ifdef FEATURE_HW_INTRINSICS GenTree* impHWIntrinsic(NamedIntrinsic intrinsic, @@ -4573,7 +4574,8 @@ class Compiler CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - GenTree* newobjThis); + GenTree* newobjThis, + bool mustExpand); protected: bool compSupportsHWIntrinsic(CORINFO_InstructionSet isa); @@ -4584,7 +4586,8 @@ class Compiler var_types retType, CorInfoType simdBaseJitType, unsigned simdSize, - GenTree* newobjThis); + GenTree* newobjThis, + bool mustExpand); GenTree* impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, @@ -4592,7 +4595,8 @@ class Compiler CORINFO_SIG_INFO* sig, CorInfoType simdBaseJitType, var_types retType, - unsigned simdSize); + unsigned simdSize, + bool mustExpand); GenTree* getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass, @@ -8241,6 +8245,22 @@ class Compiler return eeGetEEInfo()->targetAbi == abi; } + bool BlockNonDeterministicIntrinsics(bool mustExpand) + { + // We explicitly block these APIs from being expanded in R2R + // since we know they are non-deterministic across hardware + + if (opts.IsReadyToRun() && !IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + if (mustExpand) + { + implLimitation(); + } + return true; + } + return false; + } + #if defined(FEATURE_EH_WINDOWS_X86) bool eeIsNativeAotAbi; bool UsesFunclets() const diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 3187c3e5c2e04..4064939350491 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1612,7 +1612,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, } else { - retNode = impSpecialIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, nodeRetType, simdSize); + retNode = impSpecialIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, nodeRetType, simdSize, mustExpand); } #if defined(TARGET_ARM64) diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 37d129c37aa0e..5b1017ccc323d 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -335,6 +335,7 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT // sig -- signature of the intrinsic call. // simdBaseJitType -- generic argument of the intrinsic. // retType -- return type of the intrinsic. +// mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false // // Return Value: // The GT_HWINTRINSIC node, or nullptr if not a supported intrinsic @@ -345,7 +346,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_SIG_INFO* sig, CorInfoType simdBaseJitType, var_types retType, - unsigned simdSize) + unsigned simdSize, + bool mustExpand) { const HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); const int numArgs = sig->numArgs; @@ -627,10 +629,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector64_ConvertToInt32Native: case NI_Vector128_ConvertToInt32Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } FALLTHROUGH; @@ -650,10 +650,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector64_ConvertToInt64Native: case NI_Vector128_ConvertToInt64Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } FALLTHROUGH; @@ -684,10 +682,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector64_ConvertToUInt32Native: case NI_Vector128_ConvertToUInt32Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } FALLTHROUGH; @@ -707,10 +703,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector64_ConvertToUInt64Native: case NI_Vector128_ConvertToUInt64Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } FALLTHROUGH; diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 80d83e89bb6ae..d03c6ca4f840d 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -957,6 +957,8 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT // sig -- signature of the intrinsic call. // simdBaseJitType -- generic argument of the intrinsic. // retType -- return type of the intrinsic. +// mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false +// // Return Value: // the expanded intrinsic. // @@ -966,7 +968,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_SIG_INFO* sig, CorInfoType simdBaseJitType, var_types retType, - unsigned simdSize) + unsigned simdSize, + bool mustExpand) { GenTree* retNode = nullptr; GenTree* op1 = nullptr; @@ -1098,7 +1101,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, { // Vector is TYP_SIMD32, so we should treat this as a call to Vector128.ToVector256 return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, sig, simdBaseJitType, retType, - simdSize); + simdSize, mustExpand); } else if (vectorTByteLength == XMM_REGSIZE_BYTES) { @@ -1208,7 +1211,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, { // Vector is TYP_SIMD32, so we should treat this as a call to Vector256.GetLower return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize); + simdSize, mustExpand); } default: @@ -1248,13 +1251,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector256_AsVector) { return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize); + simdSize, mustExpand); } else { assert(intrinsic == NI_Vector256_AsVector256); return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, sig, simdBaseJitType, - retType, 16); + retType, 16, mustExpand); } } } @@ -1281,13 +1284,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector512_AsVector) { return impSpecialIntrinsic(NI_Vector512_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize); + simdSize, mustExpand); } else { assert(intrinsic == NI_Vector512_AsVector512); return impSpecialIntrinsic(NI_Vector256_ToVector512, clsHnd, method, sig, simdBaseJitType, retType, - 32); + 32, mustExpand); } break; } @@ -1301,13 +1304,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector512_AsVector) { return impSpecialIntrinsic(NI_Vector512_GetLower128, clsHnd, method, sig, simdBaseJitType, - retType, simdSize); + retType, simdSize, mustExpand); } else { assert(intrinsic == NI_Vector512_AsVector512); return impSpecialIntrinsic(NI_Vector128_ToVector512, clsHnd, method, sig, simdBaseJitType, - retType, 16); + retType, 16, mustExpand); } } } @@ -1436,10 +1439,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } @@ -1470,10 +1471,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } @@ -1556,10 +1555,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } @@ -1593,10 +1590,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware break; } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index c6c2bc5f94b5d..910dec90a8cdb 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3024,8 +3024,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, else { assert((ni > NI_PRIMITIVE_START) && (ni < NI_PRIMITIVE_END)); - assert(!mustExpand); - return impPrimitiveNamedIntrinsic(ni, clsHnd, method, sig); + return impPrimitiveNamedIntrinsic(ni, clsHnd, method, sig, mustExpand); } } @@ -3080,11 +3079,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, if (isIntrinsic) { - // These intrinsics aren't defined recursively and so they will never be mustExpand - // Instead, they provide software fallbacks that will be executed instead. - - assert(!mustExpand); - return impSimdAsHWIntrinsic(ni, clsHnd, method, sig, newobjThis); + return impSimdAsHWIntrinsic(ni, clsHnd, method, sig, newobjThis, mustExpand); } } } @@ -4149,7 +4144,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Math_ReciprocalEstimate: case NI_System_Math_ReciprocalSqrtEstimate: { - retNode = impEstimateIntrinsic(method, sig, callJitType, ni, tailCall); + retNode = impEstimateIntrinsic(method, sig, callJitType, ni, mustExpand); break; } @@ -5158,6 +5153,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, // clsHnd - handle for the intrinsic method's class // method - handle for the intrinsic method // sig - signature of the intrinsic method +// mustExpand - true if the intrinsic must return a GenTree*; otherwise, false // // Returns: // IR tree to use in place of the call, or nullptr if the jit should treat @@ -5166,7 +5162,8 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig) + CORINFO_SIG_INFO* sig, + bool mustExpand) { assert(sig->sigInst.classInstCount == 0); @@ -5193,10 +5190,8 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, { case NI_PRIMITIVE_ConvertToIntegerNative: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware return nullptr; } FALLTHROUGH; @@ -5527,7 +5522,7 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, } #if defined(FEATURE_HW_INTRINSICS) - GenTree* lzcnt = impPrimitiveNamedIntrinsic(NI_PRIMITIVE_LeadingZeroCount, clsHnd, method, sig); + GenTree* lzcnt = impPrimitiveNamedIntrinsic(NI_PRIMITIVE_LeadingZeroCount, clsHnd, method, sig, mustExpand); if (lzcnt != nullptr) { @@ -8762,23 +8757,21 @@ void Compiler::impCheckCanInline(GenTreeCall* call, // method - The handle of the method being imported // callType - The underlying type for the call // intrinsicName - The intrinsic being imported -// tailCall - true if the method is a tail call; otherwise false +// mustExpand - true if the intrinsic must return a GenTree*; otherwise, false // GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, CorInfoType callJitType, NamedIntrinsic intrinsicName, - bool tailCall) + bool mustExpand) { var_types callType = JITtype2varType(callJitType); assert(varTypeIsFloating(callType)); assert(sig->numArgs == 1); - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware return nullptr; } diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index d55a315e5d903..eadb82af79eff 100644 --- a/src/coreclr/jit/simdashwintrinsic.cpp +++ b/src/coreclr/jit/simdashwintrinsic.cpp @@ -253,7 +253,8 @@ GenTree* Compiler::impSimdAsHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - GenTree* newobjThis) + GenTree* newobjThis, + bool mustExpand) { if (!IsBaselineSimdIsaSupported()) { @@ -371,7 +372,8 @@ GenTree* Compiler::impSimdAsHWIntrinsic(NamedIntrinsic intrinsic, if (hwIntrinsic == intrinsic) { // The SIMD intrinsic requires special handling outside the normal code path - return impSimdAsHWIntrinsicSpecial(intrinsic, clsHnd, sig, retType, simdBaseJitType, simdSize, newobjThis); + return impSimdAsHWIntrinsicSpecial(intrinsic, clsHnd, sig, retType, simdBaseJitType, simdSize, newobjThis, + mustExpand); } CORINFO_InstructionSet hwIntrinsicIsa = HWIntrinsicInfo::lookupIsa(hwIntrinsic); @@ -442,6 +444,7 @@ GenTree* Compiler::impSimdAsHWIntrinsic(NamedIntrinsic intrinsic, // retType -- the return type of the intrinsic call // simdBaseJitType -- the base JIT type of SIMD type of the intrinsic // simdSize -- the size of the SIMD type of the intrinsic +// mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false // // Return Value: // The GT_HWINTRINSIC node, or nullptr if not a supported intrinsic @@ -452,7 +455,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, var_types retType, CorInfoType simdBaseJitType, unsigned simdSize, - GenTree* newobjThis) + GenTree* newobjThis, + bool mustExpand) { var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); @@ -514,10 +518,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, { case NI_VectorT_ConvertToInt32Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware return nullptr; } break; @@ -527,10 +529,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_VectorT_ConvertToUInt32Native: case NI_VectorT_ConvertToUInt64Native: { - if (opts.IsReadyToRun()) + if (BlockNonDeterministicIntrinsics(mustExpand)) { - // We explicitly block these APIs from being expanded in R2R - // since we know they are non-deterministic across hardware return nullptr; } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c2516a878ba98..ce22178ca8a0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -800,7 +800,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index c4f105792db51..6e6263f039049 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -663,9 +662,20 @@ public static TInteger ConvertToInteger(double value) /// [Intrinsic] - [BypassReadyToRun] public static TInteger ConvertToIntegerNative(double value) - where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + where TInteger : IBinaryInteger + { +#if !MONO + if (typeof(TInteger).IsPrimitive) + { + // We need this to be recursive so indirect calls (delegates + // for example) produce the same result as direct invocation + return ConvertToIntegerNative(value); + } +#endif + + return TInteger.CreateSaturating(value); + } /// [Intrinsic] diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index fe213516c68ce..0231d3e4ee720 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; @@ -1196,13 +1195,15 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / d. /// - [BypassReadyToRun] + [Intrinsic] + public static double ReciprocalEstimate(double d) + { #if MONO - public static double ReciprocalEstimate(double d) => 1.0 / d; + return 1.0 / d; #else - [Intrinsic] - public static double ReciprocalEstimate(double d) => ReciprocalEstimate(d); + return ReciprocalEstimate(d); #endif + } /// Returns an estimate of the reciprocal square root of a specified number. /// The number whose reciprocal square root is to be estimated. @@ -1211,13 +1212,15 @@ public static double MinMagnitude(double x, double y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(d). /// - [BypassReadyToRun] + [Intrinsic] + public static double ReciprocalSqrtEstimate(double d) + { #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 - public static double ReciprocalSqrtEstimate(double d) => 1.0 / Sqrt(d); + return 1.0 / Sqrt(d); #else - [Intrinsic] - public static double ReciprocalSqrtEstimate(double d) => ReciprocalSqrtEstimate(d); + return ReciprocalSqrtEstimate(d); #endif + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal Round(decimal d) diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 6bda367b8201c..31e7490602266 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -314,13 +313,15 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRECPE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / x. /// - [BypassReadyToRun] + [Intrinsic] + public static float ReciprocalEstimate(float x) + { #if MONO - public static float ReciprocalEstimate(float x) => 1.0f / x; + return 1.0f / x; #else - [Intrinsic] - public static float ReciprocalEstimate(float x) => ReciprocalEstimate(x); + return ReciprocalEstimate(x); #endif + } /// Returns an estimate of the reciprocal square root of a specified number. /// The number whose reciprocal square root is to be estimated. @@ -330,13 +331,15 @@ public static float MinMagnitude(float x, float y) /// On ARM64 hardware this may use the FRSQRTE instruction which performs a single Newton-Raphson iteration. /// On hardware without specialized support, this may just return 1.0 / Sqrt(x). /// - [BypassReadyToRun] + [Intrinsic] + public static float ReciprocalSqrtEstimate(float x) + { #if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 - public static float ReciprocalSqrtEstimate(float x) => 1.0f / Sqrt(x); + return 1.0f / Sqrt(x); #else - [Intrinsic] - public static float ReciprocalSqrtEstimate(float x) => ReciprocalSqrtEstimate(x); + return ReciprocalSqrtEstimate(x); #endif + } [Intrinsic] public static float Round(float x) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index bb5811457d722..8a5961bb314b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -336,7 +335,7 @@ public static Vector ConvertToInt32(Vector value) for (int i = 0; i < Vector.Count; i++) { - int element = (int)value.GetElementUnsafe(i); + int element = float.ConvertToInteger(value.GetElementUnsafe(i)); result.SetElementUnsafe(i, element); } @@ -347,8 +346,18 @@ public static Vector ConvertToInt32(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] - public static Vector ConvertToInt32Native(Vector value) => ConvertToInt32(value); + public static Vector ConvertToInt32Native(Vector value) + { + Unsafe.SkipInit(out Vector result); + + for (int i = 0; i < Vector.Count; i++) + { + int element = float.ConvertToIntegerNative(value.GetElementUnsafe(i)); + result.SetElementUnsafe(i, element); + } + + return result; + } /// Converts a to a using saturation on overflow. /// The vector to convert. @@ -360,7 +369,7 @@ public static Vector ConvertToInt64(Vector value) for (int i = 0; i < Vector.Count; i++) { - long element = (long)value.GetElementUnsafe(i); + long element = double.ConvertToInteger(value.GetElementUnsafe(i)); result.SetElementUnsafe(i, element); } @@ -371,8 +380,18 @@ public static Vector ConvertToInt64(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] - public static Vector ConvertToInt64Native(Vector value) => ConvertToInt64(value); + public static Vector ConvertToInt64Native(Vector value) + { + Unsafe.SkipInit(out Vector result); + + for (int i = 0; i < Vector.Count; i++) + { + long element = double.ConvertToIntegerNative(value.GetElementUnsafe(i)); + result.SetElementUnsafe(i, element); + } + + return result; + } /// Converts a to a . /// The vector to convert. @@ -422,7 +441,7 @@ public static Vector ConvertToUInt32(Vector value) for (int i = 0; i < Vector.Count; i++) { - uint element = (uint)value.GetElementUnsafe(i); + uint element = float.ConvertToInteger(value.GetElementUnsafe(i)); result.SetElementUnsafe(i, element); } @@ -433,9 +452,19 @@ public static Vector ConvertToUInt32(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] - public static Vector ConvertToUInt32Native(Vector value) => ConvertToUInt32(value); + public static Vector ConvertToUInt32Native(Vector value) + { + Unsafe.SkipInit(out Vector result); + + for (int i = 0; i < Vector.Count; i++) + { + uint element = float.ConvertToIntegerNative(value.GetElementUnsafe(i)); + result.SetElementUnsafe(i, element); + } + + return result; + } /// Converts a to a using saturation on overflow. /// The vector to convert. @@ -448,7 +477,7 @@ public static Vector ConvertToUInt64(Vector value) for (int i = 0; i < Vector.Count; i++) { - ulong element = (ulong)value.GetElementUnsafe(i); + ulong element = double.ConvertToInteger(value.GetElementUnsafe(i)); result.SetElementUnsafe(i, element); } @@ -459,9 +488,19 @@ public static Vector ConvertToUInt64(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] - public static Vector ConvertToUInt64Native(Vector value) => ConvertToUInt64(value); + public static Vector ConvertToUInt64Native(Vector value) + { + Unsafe.SkipInit(out Vector result); + + for (int i = 0; i < Vector.Count; i++) + { + ulong element = double.ConvertToIntegerNative(value.GetElementUnsafe(i)); + result.SetElementUnsafe(i, element); + } + + return result; + } /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. /// The type of the elements in the vector. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs deleted file mode 100644 index e9fd9f29a1cc2..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Runtime -{ - // Use this attribute to indicate that a function should never be compiled into a Ready2Run binary - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] -#if MONO - [Conditional("unnecessary")] // Mono doesn't use Ready2Run so we can remove this attribute to reduce size -#endif - internal sealed class BypassReadyToRunAttribute : Attribute - { - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 6e33be64ea8de..4080b06c92b77 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.Arm; @@ -486,7 +485,6 @@ public static unsafe Vector128 ConvertToInt32(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToInt32Native(Vector128 vector) { @@ -513,7 +511,6 @@ public static unsafe Vector128 ConvertToInt64(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToInt64Native(Vector128 vector) { @@ -611,7 +608,6 @@ public static unsafe Vector128 ConvertToUInt32(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToUInt32Native(Vector128 vector) @@ -640,7 +636,6 @@ public static unsafe Vector128 ConvertToUInt64(Vector128 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 ConvertToUInt64Native(Vector128 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index 34be4be01f9d4..4d174eef19754 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; @@ -404,7 +403,6 @@ public static Vector256 ConvertToInt32(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToInt32Native(Vector256 vector) { @@ -431,7 +429,6 @@ public static Vector256 ConvertToInt64(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToInt64Native(Vector256 vector) { @@ -519,7 +516,6 @@ public static Vector256 ConvertToUInt32(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToUInt32Native(Vector256 vector) @@ -548,7 +544,6 @@ public static Vector256 ConvertToUInt64(Vector256 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConvertToUInt64Native(Vector256 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index 7971e42dca645..d63f12afb64d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; @@ -366,7 +365,6 @@ public static Vector512 ConvertToInt32(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToInt32Native(Vector512 vector) { @@ -393,7 +391,6 @@ public static Vector512 ConvertToInt64(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToInt64Native(Vector512 vector) { @@ -448,7 +445,6 @@ public static Vector512 ConvertToUInt32(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToUInt32Native(Vector512 vector) @@ -477,7 +473,6 @@ public static Vector512 ConvertToUInt64(Vector512 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConvertToUInt64Native(Vector512 vector) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 9292f3b17a10a..f3e35f7ecc36f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.Arm; @@ -351,7 +350,7 @@ public static unsafe Vector64 ConvertToInt32(Vector64 vector) for (int i = 0; i < Vector64.Count; i++) { - int value = (int)vector.GetElementUnsafe(i); + int value = float.ConvertToInteger(vector.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } @@ -362,9 +361,19 @@ public static unsafe Vector64 ConvertToInt32(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt32Native(Vector64 vector) => ConvertToInt32(vector); + public static unsafe Vector64 ConvertToInt32Native(Vector64 vector) + { + Unsafe.SkipInit(out Vector64 result); + + for (int i = 0; i < Vector64.Count; i++) + { + int value = float.ConvertToIntegerNative(vector.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + return result; + } /// Converts a to a using saturation on overflow. /// The vector to convert. @@ -377,7 +386,7 @@ public static unsafe Vector64 ConvertToInt64(Vector64 vector) for (int i = 0; i < Vector64.Count; i++) { - long value = (long)vector.GetElementUnsafe(i); + long value = double.ConvertToInteger(vector.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } @@ -388,9 +397,19 @@ public static unsafe Vector64 ConvertToInt64(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt64Native(Vector64 vector) => ConvertToInt64(vector); + public static unsafe Vector64 ConvertToInt64Native(Vector64 vector) + { + Unsafe.SkipInit(out Vector64 result); + + for (int i = 0; i < Vector64.Count; i++) + { + long value = double.ConvertToIntegerNative(vector.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + return result; + } /// Converts a to a . /// The vector to convert. @@ -441,7 +460,7 @@ public static unsafe Vector64 ConvertToUInt32(Vector64 vector) for (int i = 0; i < Vector64.Count; i++) { - uint value = (uint)vector.GetElementUnsafe(i); + uint value = float.ConvertToInteger(vector.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } @@ -452,10 +471,20 @@ public static unsafe Vector64 ConvertToUInt32(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt32Native(Vector64 vector) => ConvertToUInt32(vector); + public static unsafe Vector64 ConvertToUInt32Native(Vector64 vector) + { + Unsafe.SkipInit(out Vector64 result); + + for (int i = 0; i < Vector64.Count; i++) + { + uint value = float.ConvertToIntegerNative(vector.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + return result; + } /// Converts a to a using saturation on overflow. /// The vector to convert. @@ -469,7 +498,7 @@ public static unsafe Vector64 ConvertToUInt64(Vector64 vector) for (int i = 0; i < Vector64.Count; i++) { - ulong value = (ulong)vector.GetElementUnsafe(i); + ulong value = double.ConvertToInteger(vector.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } @@ -480,10 +509,20 @@ public static unsafe Vector64 ConvertToUInt64(Vector64 vector) /// The vector to convert. /// The converted vector. [Intrinsic] - [BypassReadyToRun] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt64Native(Vector64 vector) => ConvertToUInt64(vector); + public static unsafe Vector64 ConvertToUInt64Native(Vector64 vector) + { + Unsafe.SkipInit(out Vector64 result); + + for (int i = 0; i < Vector64.Count; i++) + { + ulong value = double.ConvertToIntegerNative(vector.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + return result; + } /// Copies a to a given array. /// The type of the elements in the vector. diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 52ce286c58da2..bf7c06a935dfb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -658,9 +657,20 @@ public static TInteger ConvertToInteger(float value) /// [Intrinsic] - [BypassReadyToRun] public static TInteger ConvertToIntegerNative(float value) - where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + where TInteger : IBinaryInteger + { +#if !MONO + if (typeof(TInteger).IsPrimitive) + { + // We need this to be recursive so indirect calls (delegates + // for example) produce the same result as direct invocation + return ConvertToIntegerNative(value); + } +#endif + + return TInteger.CreateSaturating(value); + } /// [Intrinsic] From 91d105cf131bcaf79fd4609801b113d1cfb8645a Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 11 May 2024 09:59:06 -0700 Subject: [PATCH 08/16] Adding a regression test ensuring direct and indirect invocation of non-deterministic intrinsic APIs returns the same result --- .../JitBlue/Runtime_101731/Runtime_101731.cs | 116 ++++++++++++++++++ .../Runtime_101731/Runtime_101731.csproj | 8 ++ 2 files changed, 124 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.csproj diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.cs b/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.cs new file mode 100644 index 0000000000000..98c9f2a570ab7 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Xunit; + +public static class Runtime_101731 +{ + [Theory] + [InlineData(double.MaxValue)] + public static void TestConvertToInt32NativeDouble(double value) + { + Func func = double.ConvertToIntegerNative; + int expectedValue = double.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(float.MaxValue)] + public static void TestConvertToInt32NativeSingle(float value) + { + Func func = float.ConvertToIntegerNative; + int expectedValue = float.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(double.MaxValue)] + public static void TestConvertToInt64NativeDouble(double value) + { + Func func = double.ConvertToIntegerNative; + long expectedValue = double.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(float.MaxValue)] + public static void TestConvertToInt64NativeSingle(float value) + { + Func func = float.ConvertToIntegerNative; + long expectedValue = float.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(double.MaxValue)] + public static void TestConvertToUInt32NativeDouble(double value) + { + Func func = double.ConvertToIntegerNative; + uint expectedValue = double.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(float.MaxValue)] + public static void TestConvertToUInt32NativeSingle(float value) + { + Func func = float.ConvertToIntegerNative; + uint expectedValue = float.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(double.MaxValue)] + public static void TestConvertToUInt64NativeDouble(double value) + { + Func func = double.ConvertToIntegerNative; + ulong expectedValue = double.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(float.MaxValue)] + public static void TestConvertToUInt64NativeSingle(float value) + { + Func func = float.ConvertToIntegerNative; + ulong expectedValue = float.ConvertToIntegerNative(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(5)] + public static void TestReciprocalEstimateDouble(double value) + { + Func func = double.ReciprocalEstimate; + double expectedValue = double.ReciprocalEstimate(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(5)] + public static void TestReciprocalEstimateSingle(float value) + { + Func func = float.ReciprocalEstimate; + float expectedValue = float.ReciprocalEstimate(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(-double.Epsilon)] + public static void TestReciprocalSqrtEstimateDouble(double value) + { + Func func = double.ReciprocalSqrtEstimate; + double expectedValue = double.ReciprocalSqrtEstimate(value); + Assert.Equal(expectedValue, func(value)); + } + + [Theory] + [InlineData(-float.Epsilon)] + public static void TestReciprocalSqrtEstimateSingle(float value) + { + Func func = float.ReciprocalSqrtEstimate; + float expectedValue = float.ReciprocalSqrtEstimate(value); + Assert.Equal(expectedValue, func(value)); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.csproj new file mode 100644 index 0000000000000..de6d5e08882e8 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.csproj @@ -0,0 +1,8 @@ + + + True + + + + + From ed406bf1cba7cd902386e6b9a1f5d3e85113845f Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 11 May 2024 10:09:23 -0700 Subject: [PATCH 09/16] Add a note about non-deterministic intrinsic expansion to the botr --- docs/design/coreclr/botr/vectors-and-intrinsics.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index 6b15c16981c93..7e8f597e791e6 100644 --- a/docs/design/coreclr/botr/vectors-and-intrinsics.md +++ b/docs/design/coreclr/botr/vectors-and-intrinsics.md @@ -156,6 +156,14 @@ private void SomeVectorizationHelper() } ``` +#### Non-Deterministic Intrinsics in System.Private.Corelib + +Some APIs exposed in System.Private.Corelib are intentionally non-deterministic across hardware and instead only ensure determinism within the scope of a single process. To facilitate the support of such APIs, the JIT defines `Compiler::BlockNonDeterministicIntrinsics(bool mustExpand)` which should be used to help block such APIs from expanding in scenarios such as ReadyToRun. Additionally, such APIs should recursively call themslves so that indirect invocation (such as via a delegate, function pointer, reflection, etc) will compute the same result. + +An example of such a non-deterministic API is the `ConvertToIntegerNative` APIs exposed on `System.Single` and `System.Double`. These APIs convert from the source value to the target integer type using the fastest mechanism available for the underlying hardware. They exist due to the IEEE 754 specification leaving conversions undefined when the input cannot fit into the output (for example converting `float.MaxValue` to `int`) and thus different hardware having historically provided differing behaviors on these edge cases. They allow developers who do not need to be concerned with edge case handling but where the performance overhead of normalizing results for the default cast operator is too great. + +Another example is the various `*Estimate` APIs, such as `float.ReciprocalSqrtEstimate`. These APIs allow a user to likewise opt into a faster result at the cost of some inaccuracy, where the exact inaccuracy encountered depends on the input and the underlying hardware the instruction is executed against. + # Mechanisms in the JIT to generate correct code to handle varied instruction set support The JIT receives flags which instruct it on what instruction sets are valid to use, and has access to a new jit interface api `notifyInstructionSetUsage(isa, bool supportBehaviorRequired)`. From 1b16a09e751186ad2eac1fa9d160a61b35909b29 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 11 May 2024 10:31:23 -0700 Subject: [PATCH 10/16] Apply formatting patch --- docs/design/coreclr/botr/vectors-and-intrinsics.md | 2 +- src/coreclr/jit/hwintrinsic.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index 7e8f597e791e6..2fc93df7e8ee6 100644 --- a/docs/design/coreclr/botr/vectors-and-intrinsics.md +++ b/docs/design/coreclr/botr/vectors-and-intrinsics.md @@ -158,7 +158,7 @@ private void SomeVectorizationHelper() #### Non-Deterministic Intrinsics in System.Private.Corelib -Some APIs exposed in System.Private.Corelib are intentionally non-deterministic across hardware and instead only ensure determinism within the scope of a single process. To facilitate the support of such APIs, the JIT defines `Compiler::BlockNonDeterministicIntrinsics(bool mustExpand)` which should be used to help block such APIs from expanding in scenarios such as ReadyToRun. Additionally, such APIs should recursively call themslves so that indirect invocation (such as via a delegate, function pointer, reflection, etc) will compute the same result. +Some APIs exposed in System.Private.Corelib are intentionally non-deterministic across hardware and instead only ensure determinism within the scope of a single process. To facilitate the support of such APIs, the JIT defines `Compiler::BlockNonDeterministicIntrinsics(bool mustExpand)` which should be used to help block such APIs from expanding in scenarios such as ReadyToRun. Additionally, such APIs should recursively call themselves so that indirect invocation (such as via a delegate, function pointer, reflection, etc) will compute the same result. An example of such a non-deterministic API is the `ConvertToIntegerNative` APIs exposed on `System.Single` and `System.Double`. These APIs convert from the source value to the target integer type using the fastest mechanism available for the underlying hardware. They exist due to the IEEE 754 specification leaving conversions undefined when the input cannot fit into the output (for example converting `float.MaxValue` to `int`) and thus different hardware having historically provided differing behaviors on these edge cases. They allow developers who do not need to be concerned with edge case handling but where the performance overhead of normalizing results for the default cast operator is too great. diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 4064939350491..64f34f2083412 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1612,7 +1612,8 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, } else { - retNode = impSpecialIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, nodeRetType, simdSize, mustExpand); + retNode = + impSpecialIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, nodeRetType, simdSize, mustExpand); } #if defined(TARGET_ARM64) From 0312112562a8342742fdfe3a3d0180e21c131702 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 08:14:33 -0700 Subject: [PATCH 11/16] Ensure vector tests are correctly validating against the scalar implementation --- .../tests/GenericVectorTests.cs | 8 +- .../tests/Vectors/Vector128Tests.cs | 79 ++++++------------- .../tests/Vectors/Vector256Tests.cs | 79 ++++++------------- .../tests/Vectors/Vector512Tests.cs | 79 ++++++------------- .../tests/Vectors/Vector64Tests.cs | 48 +++++------ 5 files changed, 100 insertions(+), 193 deletions(-) diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 367d2f9669964..97a65f0a84173 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -3164,7 +3164,7 @@ public void ConvertSingleToInt32() Vector targetVec = Vector.ConvertToInt32(sourceVec); for (int i = 0; i < Vector.Count; i++) { - Assert.Equal(unchecked((int)source[i]), targetVec[i]); + Assert.Equal(float.ConvertToInteger(source[i]), targetVec[i]); } } @@ -3176,7 +3176,7 @@ public void ConvertSingleToUInt32() Vector targetVec = Vector.ConvertToUInt32(sourceVec); for (int i = 0; i < Vector.Count; i++) { - Assert.Equal(unchecked((uint)source[i]), targetVec[i]); + Assert.Equal(float.ConvertToInteger(source[i]), targetVec[i]); } } @@ -3188,7 +3188,7 @@ public void ConvertDoubleToInt64() Vector targetVec = Vector.ConvertToInt64(sourceVec); for (int i = 0; i < Vector.Count; i++) { - Assert.Equal(unchecked((long)source[i]), targetVec[i]); + Assert.Equal(double.ConvertToInteger(source[i]), targetVec[i]); } } @@ -3200,7 +3200,7 @@ public void ConvertDoubleToUInt64() Vector targetVec = Vector.ConvertToUInt64(sourceVec); for (int i = 0; i < Vector.Count; i++) { - Assert.Equal(unchecked((ulong)source[i]), targetVec[i]); + Assert.Equal(double.ConvertToInteger(source[i]), targetVec[i]); } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 95cf6d16b6617..fab2082083ab7 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -4878,103 +4878,72 @@ public void Log2SingleTest(float value, float expectedResult, float variance) [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32Test() { - Assert.Equal(Vector128.Create(int.MinValue), Vector128.ConvertToInt32(Vector128.Create(float.MinValue))); - Assert.Equal(Vector128.Create(2), Vector128.ConvertToInt32(Vector128.Create(2.6f))); - Assert.Equal(Vector128.Create(int.MaxValue), Vector128.ConvertToInt32(Vector128.Create(float.MaxValue))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(float.MinValue)), Vector128.ConvertToInt32(Vector128.Create(float.MinValue))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(2.6f)), Vector128.ConvertToInt32(Vector128.Create(2.6f))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(float.MaxValue)), Vector128.ConvertToInt32(Vector128.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Sse2.IsSupported) - { - Assert.Equal(Vector128.Create(int.MinValue), Vector128.ConvertToInt32Native(Vector128.Create(float.MaxValue))); - } - else - { - Assert.Equal(Vector128.Create(int.MaxValue), Vector128.ConvertToInt32Native(Vector128.Create(float.MaxValue))); - } - Assert.Equal(Vector128.Create(int.MinValue), Vector128.ConvertToInt32Native(Vector128.Create(float.MinValue))); - Assert.Equal(Vector128.Create(2), Vector128.ConvertToInt32Native(Vector128.Create(2.6f))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(float.MinValue)), Vector128.ConvertToInt32Native(Vector128.Create(float.MinValue))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(2.6f)), Vector128.ConvertToInt32Native(Vector128.Create(2.6f))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector128.ConvertToInt32Native(Vector128.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64Test() { - Assert.Equal(Vector128.Create(long.MinValue), Vector128.ConvertToInt64(Vector128.Create(double.MinValue))); - Assert.Equal(Vector128.Create(2L), Vector128.ConvertToInt64(Vector128.Create(2.6))); - Assert.Equal(Vector128.Create(long.MaxValue), Vector128.ConvertToInt64(Vector128.Create(double.MaxValue))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(double.MinValue)), Vector128.ConvertToInt64(Vector128.Create(double.MinValue))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(2.6)), Vector128.ConvertToInt64(Vector128.Create(2.6))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(double.MaxValue)), Vector128.ConvertToInt64(Vector128.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector128.Create(long.MinValue), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); - } - else - { - Assert.Equal(Vector128.Create(long.MaxValue), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); - } - - Assert.Equal(Vector128.Create(long.MinValue), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue))); - Assert.Equal(Vector128.Create(2L), Vector128.ConvertToInt64Native(Vector128.Create(2.6))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(2.6)), Vector128.ConvertToInt64Native(Vector128.Create(2.6))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32Test() { - Assert.Equal(Vector128.Create(uint.MinValue), Vector128.ConvertToUInt32(Vector128.Create(float.MinValue))); - Assert.Equal(Vector128.Create(2u), Vector128.ConvertToUInt32(Vector128.Create(2.6f))); - Assert.Equal(Vector128.Create(uint.MaxValue), Vector128.ConvertToUInt32(Vector128.Create(float.MaxValue))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(float.MinValue)), Vector128.ConvertToUInt32(Vector128.Create(float.MinValue))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(2.6f)), Vector128.ConvertToUInt32(Vector128.Create(2.6f))); + Assert.Equal(Vector128.Create(float.ConvertToInteger(float.MaxValue)), Vector128.ConvertToUInt32(Vector128.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512F.VL.IsSupported) - { - Assert.Equal(Vector128.Create(uint.MaxValue), Vector128.ConvertToUInt32Native(Vector128.Create(float.MinValue))); - } - else - { - Assert.Equal(Vector128.Create(uint.MinValue), Vector128.ConvertToUInt32Native(Vector128.Create(float.MinValue))); - } - - Assert.Equal(Vector128.Create(2u), Vector128.ConvertToUInt32Native(Vector128.Create(2.6f))); - Assert.Equal(Vector128.Create(uint.MaxValue), Vector128.ConvertToUInt32Native(Vector128.Create(float.MaxValue))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(float.MinValue)), Vector128.ConvertToUInt32Native(Vector128.Create(float.MinValue))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(2.6f)), Vector128.ConvertToUInt32Native(Vector128.Create(2.6f))); + Assert.Equal(Vector128.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector128.ConvertToUInt32Native(Vector128.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64Test() { - Assert.Equal(Vector128.Create(ulong.MinValue), Vector128.ConvertToUInt64(Vector128.Create(double.MinValue))); - Assert.Equal(Vector128.Create(2UL), Vector128.ConvertToUInt64(Vector128.Create(2.6))); - Assert.Equal(Vector128.Create(ulong.MaxValue), Vector128.ConvertToUInt64(Vector128.Create(double.MaxValue))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(double.MinValue)), Vector128.ConvertToUInt64(Vector128.Create(double.MinValue))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(2.6)), Vector128.ConvertToUInt64(Vector128.Create(2.6))); + Assert.Equal(Vector128.Create(double.ConvertToInteger(double.MaxValue)), Vector128.ConvertToUInt64(Vector128.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector128.Create(ulong.MaxValue), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); - } - else - { - Assert.Equal(Vector128.Create(ulong.MinValue), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); - } - - Assert.Equal(Vector128.Create(2UL), Vector128.ConvertToUInt64Native(Vector128.Create(2.6))); - Assert.Equal(Vector128.Create(ulong.MaxValue), Vector128.ConvertToUInt64Native(Vector128.Create(double.MaxValue))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(2.6)), Vector128.ConvertToUInt64Native(Vector128.Create(2.6))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector128.ConvertToUInt64Native(Vector128.Create(double.MaxValue))); } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index a5559d43c8150..488b15fe6f845 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -5893,103 +5893,72 @@ public void Log2SingleTest(float value, float expectedResult, float variance) [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32Test() { - Assert.Equal(Vector256.Create(int.MinValue), Vector256.ConvertToInt32(Vector256.Create(float.MinValue))); - Assert.Equal(Vector256.Create(2), Vector256.ConvertToInt32(Vector256.Create(2.6f))); - Assert.Equal(Vector256.Create(int.MaxValue), Vector256.ConvertToInt32(Vector256.Create(float.MaxValue))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(float.MinValue)), Vector256.ConvertToInt32(Vector256.Create(float.MinValue))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(2.6f)), Vector256.ConvertToInt32(Vector256.Create(2.6f))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(float.MaxValue)), Vector256.ConvertToInt32(Vector256.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Sse2.IsSupported) - { - Assert.Equal(Vector256.Create(int.MinValue), Vector256.ConvertToInt32Native(Vector256.Create(float.MaxValue))); - } - else - { - Assert.Equal(Vector256.Create(int.MaxValue), Vector256.ConvertToInt32Native(Vector256.Create(float.MaxValue))); - } - Assert.Equal(Vector256.Create(int.MinValue), Vector256.ConvertToInt32Native(Vector256.Create(float.MinValue))); - Assert.Equal(Vector256.Create(2), Vector256.ConvertToInt32Native(Vector256.Create(2.6f))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(float.MinValue)), Vector256.ConvertToInt32Native(Vector256.Create(float.MinValue))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(2.6f)), Vector256.ConvertToInt32Native(Vector256.Create(2.6f))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector256.ConvertToInt32Native(Vector256.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64Test() { - Assert.Equal(Vector256.Create(long.MinValue), Vector256.ConvertToInt64(Vector256.Create(double.MinValue))); - Assert.Equal(Vector256.Create(2L), Vector256.ConvertToInt64(Vector256.Create(2.6))); - Assert.Equal(Vector256.Create(long.MaxValue), Vector256.ConvertToInt64(Vector256.Create(double.MaxValue))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(double.MinValue)), Vector256.ConvertToInt64(Vector256.Create(double.MinValue))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(2.6)), Vector256.ConvertToInt64(Vector256.Create(2.6))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(double.MaxValue)), Vector256.ConvertToInt64(Vector256.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector256.Create(long.MinValue), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); - } - else - { - Assert.Equal(Vector256.Create(long.MaxValue), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); - } - - Assert.Equal(Vector256.Create(long.MinValue), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue))); - Assert.Equal(Vector256.Create(2L), Vector256.ConvertToInt64Native(Vector256.Create(2.6))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(2.6)), Vector256.ConvertToInt64Native(Vector256.Create(2.6))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32Test() { - Assert.Equal(Vector256.Create(uint.MinValue), Vector256.ConvertToUInt32(Vector256.Create(float.MinValue))); - Assert.Equal(Vector256.Create(2u), Vector256.ConvertToUInt32(Vector256.Create(2.6f))); - Assert.Equal(Vector256.Create(uint.MaxValue), Vector256.ConvertToUInt32(Vector256.Create(float.MaxValue))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(float.MinValue)), Vector256.ConvertToUInt32(Vector256.Create(float.MinValue))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(2.6f)), Vector256.ConvertToUInt32(Vector256.Create(2.6f))); + Assert.Equal(Vector256.Create(float.ConvertToInteger(float.MaxValue)), Vector256.ConvertToUInt32(Vector256.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512F.VL.IsSupported) - { - Assert.Equal(Vector256.Create(uint.MaxValue), Vector256.ConvertToUInt32Native(Vector256.Create(float.MinValue))); - } - else - { - Assert.Equal(Vector256.Create(uint.MinValue), Vector256.ConvertToUInt32Native(Vector256.Create(float.MinValue))); - } - - Assert.Equal(Vector256.Create(2u), Vector256.ConvertToUInt32Native(Vector256.Create(2.6f))); - Assert.Equal(Vector256.Create(uint.MaxValue), Vector256.ConvertToUInt32Native(Vector256.Create(float.MaxValue))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(float.MinValue)), Vector256.ConvertToUInt32Native(Vector256.Create(float.MinValue))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(2.6f)), Vector256.ConvertToUInt32Native(Vector256.Create(2.6f))); + Assert.Equal(Vector256.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector256.ConvertToUInt32Native(Vector256.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64Test() { - Assert.Equal(Vector256.Create(ulong.MinValue), Vector256.ConvertToUInt64(Vector256.Create(double.MinValue))); - Assert.Equal(Vector256.Create(2UL), Vector256.ConvertToUInt64(Vector256.Create(2.6))); - Assert.Equal(Vector256.Create(ulong.MaxValue), Vector256.ConvertToUInt64(Vector256.Create(double.MaxValue))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(double.MinValue)), Vector256.ConvertToUInt64(Vector256.Create(double.MinValue))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(2.6)), Vector256.ConvertToUInt64(Vector256.Create(2.6))); + Assert.Equal(Vector256.Create(double.ConvertToInteger(double.MaxValue)), Vector256.ConvertToUInt64(Vector256.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector256.Create(ulong.MaxValue), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); - } - else - { - Assert.Equal(Vector256.Create(ulong.MinValue), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); - } - - Assert.Equal(Vector256.Create(2UL), Vector256.ConvertToUInt64Native(Vector256.Create(2.6))); - Assert.Equal(Vector256.Create(ulong.MaxValue), Vector256.ConvertToUInt64Native(Vector256.Create(double.MaxValue))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(2.6)), Vector256.ConvertToUInt64Native(Vector256.Create(2.6))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector256.ConvertToUInt64Native(Vector256.Create(double.MaxValue))); } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index c01f88facbf57..eab4e0395bcbc 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5326,103 +5326,72 @@ public void Log2SingleTest(float value, float expectedResult, float variance) [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32Test() { - Assert.Equal(Vector512.Create(int.MinValue), Vector512.ConvertToInt32(Vector512.Create(float.MinValue))); - Assert.Equal(Vector512.Create(2), Vector512.ConvertToInt32(Vector512.Create(2.6f))); - Assert.Equal(Vector512.Create(int.MaxValue), Vector512.ConvertToInt32(Vector512.Create(float.MaxValue))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(float.MinValue)), Vector512.ConvertToInt32(Vector512.Create(float.MinValue))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(2.6f)), Vector512.ConvertToInt32(Vector512.Create(2.6f))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(float.MaxValue)), Vector512.ConvertToInt32(Vector512.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Sse2.IsSupported) - { - Assert.Equal(Vector512.Create(int.MinValue), Vector512.ConvertToInt32Native(Vector512.Create(float.MaxValue))); - } - else - { - Assert.Equal(Vector512.Create(int.MaxValue), Vector512.ConvertToInt32Native(Vector512.Create(float.MaxValue))); - } - Assert.Equal(Vector512.Create(int.MinValue), Vector512.ConvertToInt32Native(Vector512.Create(float.MinValue))); - Assert.Equal(Vector512.Create(2), Vector512.ConvertToInt32Native(Vector512.Create(2.6f))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(float.MinValue)), Vector512.ConvertToInt32Native(Vector512.Create(float.MinValue))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(2.6f)), Vector512.ConvertToInt32Native(Vector512.Create(2.6f))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector512.ConvertToInt32Native(Vector512.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64Test() { - Assert.Equal(Vector512.Create(long.MinValue), Vector512.ConvertToInt64(Vector512.Create(double.MinValue))); - Assert.Equal(Vector512.Create(2L), Vector512.ConvertToInt64(Vector512.Create(2.6))); - Assert.Equal(Vector512.Create(long.MaxValue), Vector512.ConvertToInt64(Vector512.Create(double.MaxValue))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(double.MinValue)), Vector512.ConvertToInt64(Vector512.Create(double.MinValue))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(2.6)), Vector512.ConvertToInt64(Vector512.Create(2.6))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(double.MaxValue)), Vector512.ConvertToInt64(Vector512.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector512.Create(long.MinValue), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); - } - else - { - Assert.Equal(Vector512.Create(long.MaxValue), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); - } - - Assert.Equal(Vector512.Create(long.MinValue), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue))); - Assert.Equal(Vector512.Create(2L), Vector512.ConvertToInt64Native(Vector512.Create(2.6))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(2.6)), Vector512.ConvertToInt64Native(Vector512.Create(2.6))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32Test() { - Assert.Equal(Vector512.Create(uint.MinValue), Vector512.ConvertToUInt32(Vector512.Create(float.MinValue))); - Assert.Equal(Vector512.Create(2u), Vector512.ConvertToUInt32(Vector512.Create(2.6f))); - Assert.Equal(Vector512.Create(uint.MaxValue), Vector512.ConvertToUInt32(Vector512.Create(float.MaxValue))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(float.MinValue)), Vector512.ConvertToUInt32(Vector512.Create(float.MinValue))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(2.6f)), Vector512.ConvertToUInt32(Vector512.Create(2.6f))); + Assert.Equal(Vector512.Create(float.ConvertToInteger(float.MaxValue)), Vector512.ConvertToUInt32(Vector512.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512F.VL.IsSupported) - { - Assert.Equal(Vector512.Create(uint.MaxValue), Vector512.ConvertToUInt32Native(Vector512.Create(float.MinValue))); - } - else - { - Assert.Equal(Vector512.Create(uint.MinValue), Vector512.ConvertToUInt32Native(Vector512.Create(float.MinValue))); - } - - Assert.Equal(Vector512.Create(2u), Vector512.ConvertToUInt32Native(Vector512.Create(2.6f))); - Assert.Equal(Vector512.Create(uint.MaxValue), Vector512.ConvertToUInt32Native(Vector512.Create(float.MaxValue))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(float.MinValue)), Vector512.ConvertToUInt32Native(Vector512.Create(float.MinValue))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(2.6f)), Vector512.ConvertToUInt32Native(Vector512.Create(2.6f))); + Assert.Equal(Vector512.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector512.ConvertToUInt32Native(Vector512.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64Test() { - Assert.Equal(Vector512.Create(ulong.MinValue), Vector512.ConvertToUInt64(Vector512.Create(double.MinValue))); - Assert.Equal(Vector512.Create(2UL), Vector512.ConvertToUInt64(Vector512.Create(2.6))); - Assert.Equal(Vector512.Create(ulong.MaxValue), Vector512.ConvertToUInt64(Vector512.Create(double.MaxValue))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(double.MinValue)), Vector512.ConvertToUInt64(Vector512.Create(double.MinValue))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(2.6)), Vector512.ConvertToUInt64(Vector512.Create(2.6))); + Assert.Equal(Vector512.Create(double.ConvertToInteger(double.MaxValue)), Vector512.ConvertToUInt64(Vector512.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - if (Vector128.IsHardwareAccelerated && Avx512DQ.VL.IsSupported) - { - Assert.Equal(Vector512.Create(ulong.MaxValue), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); - } - else - { - Assert.Equal(Vector512.Create(ulong.MinValue), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); - } - - Assert.Equal(Vector512.Create(2UL), Vector512.ConvertToUInt64Native(Vector512.Create(2.6))); - Assert.Equal(Vector512.Create(ulong.MaxValue), Vector512.ConvertToUInt64Native(Vector512.Create(double.MaxValue))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(2.6)), Vector512.ConvertToUInt64Native(Vector512.Create(2.6))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector512.ConvertToUInt64Native(Vector512.Create(double.MaxValue))); } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index 3c2d8064681fe..18c1d3b05af17 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -4293,72 +4293,72 @@ public void Log2SingleTest(float value, float expectedResult, float variance) [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32Test() { - Assert.Equal(Vector64.Create(int.MinValue), Vector64.ConvertToInt32(Vector64.Create(float.MinValue))); - Assert.Equal(Vector64.Create(2), Vector64.ConvertToInt32(Vector64.Create(2.6f))); - Assert.Equal(Vector64.Create(int.MaxValue), Vector64.ConvertToInt32(Vector64.Create(float.MaxValue))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(float.MinValue)), Vector64.ConvertToInt32(Vector64.Create(float.MinValue))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(2.6f)), Vector64.ConvertToInt32(Vector64.Create(2.6f))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(float.MaxValue)), Vector64.ConvertToInt32(Vector64.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt32NativeTest() { - Assert.Equal(Vector64.Create(int.MinValue), Vector64.ConvertToInt32Native(Vector64.Create(float.MinValue))); - Assert.Equal(Vector64.Create(2), Vector64.ConvertToInt32Native(Vector64.Create(2.6f))); - Assert.Equal(Vector64.Create(int.MaxValue), Vector64.ConvertToInt32Native(Vector64.Create(float.MaxValue))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(float.MinValue)), Vector64.ConvertToInt32Native(Vector64.Create(float.MinValue))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(2.6f)), Vector64.ConvertToInt32Native(Vector64.Create(2.6f))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector64.ConvertToInt32Native(Vector64.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64Test() { - Assert.Equal(Vector64.Create(long.MinValue), Vector64.ConvertToInt64(Vector64.Create(double.MinValue))); - Assert.Equal(Vector64.Create(2L), Vector64.ConvertToInt64(Vector64.Create(2.6))); - Assert.Equal(Vector64.Create(long.MaxValue), Vector64.ConvertToInt64(Vector64.Create(double.MaxValue))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(double.MinValue)), Vector64.ConvertToInt64(Vector64.Create(double.MinValue))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(2.6)), Vector64.ConvertToInt64(Vector64.Create(2.6))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(double.MaxValue)), Vector64.ConvertToInt64(Vector64.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToInt64NativeTest() { - Assert.Equal(Vector64.Create(long.MinValue), Vector64.ConvertToInt64Native(Vector64.Create(double.MinValue))); - Assert.Equal(Vector64.Create(2L), Vector64.ConvertToInt64Native(Vector64.Create(2.6))); - Assert.Equal(Vector64.Create(long.MaxValue), Vector64.ConvertToInt64Native(Vector64.Create(double.MaxValue))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(double.MinValue)), Vector64.ConvertToInt64Native(Vector64.Create(double.MinValue))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(2.6)), Vector64.ConvertToInt64Native(Vector64.Create(2.6))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector64.ConvertToInt64Native(Vector64.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32Test() { - Assert.Equal(Vector64.Create(uint.MinValue), Vector64.ConvertToUInt32(Vector64.Create(float.MinValue))); - Assert.Equal(Vector64.Create(2u), Vector64.ConvertToUInt32(Vector64.Create(2.6f))); - Assert.Equal(Vector64.Create(uint.MaxValue), Vector64.ConvertToUInt32(Vector64.Create(float.MaxValue))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(float.MinValue)), Vector64.ConvertToUInt32(Vector64.Create(float.MinValue))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(2.6f)), Vector64.ConvertToUInt32(Vector64.Create(2.6f))); + Assert.Equal(Vector64.Create(float.ConvertToInteger(float.MaxValue)), Vector64.ConvertToUInt32(Vector64.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt32NativeTest() { - Assert.Equal(Vector64.Create(uint.MinValue), Vector64.ConvertToUInt32Native(Vector64.Create(float.MinValue))); - Assert.Equal(Vector64.Create(2u), Vector64.ConvertToUInt32Native(Vector64.Create(2.6f))); - Assert.Equal(Vector64.Create(uint.MaxValue), Vector64.ConvertToUInt32Native(Vector64.Create(float.MaxValue))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(float.MinValue)), Vector64.ConvertToUInt32Native(Vector64.Create(float.MinValue))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(2.6f)), Vector64.ConvertToUInt32Native(Vector64.Create(2.6f))); + Assert.Equal(Vector64.Create(float.ConvertToIntegerNative(float.MaxValue)), Vector64.ConvertToUInt32Native(Vector64.Create(float.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64Test() { - Assert.Equal(Vector64.Create(ulong.MinValue), Vector64.ConvertToUInt64(Vector64.Create(double.MinValue))); - Assert.Equal(Vector64.Create(2UL), Vector64.ConvertToUInt64(Vector64.Create(2.6))); - Assert.Equal(Vector64.Create(ulong.MaxValue), Vector64.ConvertToUInt64(Vector64.Create(double.MaxValue))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(double.MinValue)), Vector64.ConvertToUInt64(Vector64.Create(double.MinValue))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(2.6)), Vector64.ConvertToUInt64(Vector64.Create(2.6))); + Assert.Equal(Vector64.Create(double.ConvertToInteger(double.MaxValue)), Vector64.ConvertToUInt64(Vector64.Create(double.MaxValue))); } [Fact] [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - Assert.Equal(Vector64.Create(ulong.MinValue), Vector64.ConvertToUInt64Native(Vector64.Create(double.MinValue))); - Assert.Equal(Vector64.Create(2UL), Vector64.ConvertToUInt64Native(Vector64.Create(2.6))); - Assert.Equal(Vector64.Create(ulong.MaxValue), Vector64.ConvertToUInt64Native(Vector64.Create(double.MaxValue))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(double.MinValue)), Vector64.ConvertToUInt64Native(Vector64.Create(double.MinValue))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(2.6)), Vector64.ConvertToUInt64Native(Vector64.Create(2.6))); + Assert.Equal(Vector64.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector64.ConvertToUInt64Native(Vector64.Create(double.MaxValue))); } } } From 461c4b570c1800387d8157a367000fb5f2719b90 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 11:44:03 -0700 Subject: [PATCH 12/16] Fix the JIT/SIMD/VectorConvert test and workaround a 32-bit test issue --- .../tests/Vectors/Vector128Tests.cs | 14 ++++++- .../tests/Vectors/Vector256Tests.cs | 14 ++++++- .../tests/Vectors/Vector512Tests.cs | 14 ++++++- src/tests/JIT/SIMD/VectorConvert.cs | 42 ++++++++++--------- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index fab2082083ab7..d01939ec548f9 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -4907,7 +4907,12 @@ public void ConvertToInt64NativeTest() { Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue))); Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(2.6)), Vector128.ConvertToInt64Native(Vector128.Create(2.6))); - Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); + + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); + } } [Fact] @@ -4941,7 +4946,12 @@ public void ConvertToUInt64Test() [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); + } + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(2.6)), Vector128.ConvertToUInt64Native(Vector128.Create(2.6))); Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector128.ConvertToUInt64Native(Vector128.Create(double.MaxValue))); } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index 488b15fe6f845..d3698db4e5b23 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -5922,7 +5922,12 @@ public void ConvertToInt64NativeTest() { Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue))); Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(2.6)), Vector256.ConvertToInt64Native(Vector256.Create(2.6))); - Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); + + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); + } } [Fact] @@ -5956,7 +5961,12 @@ public void ConvertToUInt64Test() [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); + } + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(2.6)), Vector256.ConvertToUInt64Native(Vector256.Create(2.6))); Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector256.ConvertToUInt64Native(Vector256.Create(double.MaxValue))); } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index eab4e0395bcbc..f4cbb180fce9f 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5355,7 +5355,12 @@ public void ConvertToInt64NativeTest() { Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue))); Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(2.6)), Vector512.ConvertToInt64Native(Vector512.Create(2.6))); - Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); + + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); + } } [Fact] @@ -5389,7 +5394,12 @@ public void ConvertToUInt64Test() [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public void ConvertToUInt64NativeTest() { - Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); + if (Environment.Is64BitProcess) + { + // This isn't accelerated on all 32-bit systems today and may fallback to ConvertToInteger behavior + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); + } + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(2.6)), Vector512.ConvertToUInt64Native(Vector512.Create(2.6))); Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(double.MaxValue)), Vector512.ConvertToUInt64Native(Vector512.Create(double.MaxValue))); } diff --git a/src/tests/JIT/SIMD/VectorConvert.cs b/src/tests/JIT/SIMD/VectorConvert.cs index 9bd57b9400898..3692cfe127725 100644 --- a/src/tests/JIT/SIMD/VectorConvert.cs +++ b/src/tests/JIT/SIMD/VectorConvert.cs @@ -147,8 +147,9 @@ public static int VectorConvertSingleInt(Vector A) int returnVal = Pass; for (int i = 0; i < Vector.Count; i++) { - Int32 int32Val = (Int32)A[i]; + Int32 int32Val = float.ConvertToInteger(A[i]); Single cvtSglVal = (Single)int32Val; + if (B[i] != int32Val) { Console.WriteLine("B[" + i + "] = " + B[i] + ", int32Val = " + int32Val); @@ -171,8 +172,9 @@ public static int VectorConvertSingleUInt(Vector A) int returnVal = Pass; for (int i = 0; i < Vector.Count; i++) { - UInt32 uint32Val = (UInt32)A[i]; + UInt32 uint32Val = float.ConvertToInteger(A[i]); Single cvtSglVal = (Single)uint32Val; + if ((B[i] != uint32Val) || (C[i] != cvtSglVal)) { Console.WriteLine("A[{0}] = {1}, B[{0}] = {2}, C[{0}] = {3}, uint32Val = {4}, cvtSglVal = {5}", @@ -191,8 +193,9 @@ public static int VectorConvertDoubleInt64(Vector A) int returnVal = Pass; for (int i = 0; i < Vector.Count; i++) { - Int64 int64Val = (Int64)A[i]; + Int64 int64Val = double.ConvertToInteger(A[i]); Double cvtDblVal = (Double)int64Val; + if (B[i] != int64Val) { Console.WriteLine("B[" + i + "] = " + B[i] + ", int64Val = " + int64Val); @@ -215,8 +218,9 @@ public static int VectorConvertDoubleUInt64(Vector A) int returnVal = Pass; for (int i = 0; i < Vector.Count; i++) { - UInt64 uint64Val = (UInt64)A[i]; + UInt64 uint64Val = double.ConvertToInteger(A[i]); Double cvtDblVal = (Double)uint64Val; + if ((B[i] != uint64Val) || (C[i] != cvtDblVal)) { Console.WriteLine("A[{0}] = {1}, B[{0}] = {2}, C[{0}] = {3}, uint64Val = {4}, cvtDblVal = {5}", @@ -340,7 +344,7 @@ public static int VectorConvertInt32And16(Vector A1, Vector A2) } return returnVal; } - + public static int VectorConvertInt16And8(Vector A1, Vector A2) { Vector B = Vector.Narrow(A1, A2); @@ -378,7 +382,7 @@ public static int VectorConvertInt16And8(Vector A1, Vector A2) } return returnVal; } - + public static int VectorConvertUInt64And32(Vector A1, Vector A2) { Vector B = Vector.Narrow(A1, A2); @@ -454,7 +458,7 @@ public static int VectorConvertUInt32And16(Vector A1, Vector A2) } return returnVal; } - + public static int VectorConvertUInt16And8(Vector A1, Vector A2) { Vector B = Vector.Narrow(A1, A2); @@ -508,7 +512,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector singleVector = getRandomVector(singles, i); @@ -518,7 +522,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector = getRandomVector(doubles, i); @@ -528,7 +532,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector = getRandomVector(doubles, i); @@ -538,7 +542,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector1 = getRandomVector(doubles, i); @@ -549,7 +553,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int64Vector1 = getRandomVector(int64s, i); @@ -560,7 +564,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int32Vector1 = getRandomVector(int32s, i); @@ -571,7 +575,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int16Vector1 = getRandomVector(int16s, i); @@ -582,7 +586,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint64Vector1 = getRandomVector(uint64s, i); @@ -593,7 +597,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint32Vector1 = getRandomVector(uint32s, i); @@ -604,7 +608,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint16Vector1 = getRandomVector(uint16s, i); @@ -616,9 +620,9 @@ public static int TestEntryPoint() } } - JitLog jitLog = new JitLog(); + JitLog jitLog = new JitLog(); // SIMD conversions from floating point to unsigned are not supported on x86 or x64 - + if (!jitLog.Check("System.Numerics.Vector:ConvertToInt32(struct):struct")) returnVal = Fail; if (!jitLog.Check("System.Numerics.Vector:ConvertToSingle(struct):struct")) returnVal = Fail; // SIMD Conversion to Int64 is not supported on x86 From 9983d9d98139f29873a2eac15b62ac8bac7ea6ab Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 13:51:30 -0700 Subject: [PATCH 13/16] Skip a test on Mono due to a known/tracked issue --- src/tests/JIT/SIMD/VectorConvert.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/JIT/SIMD/VectorConvert.cs b/src/tests/JIT/SIMD/VectorConvert.cs index 3692cfe127725..6c1c4628bbb64 100644 --- a/src/tests/JIT/SIMD/VectorConvert.cs +++ b/src/tests/JIT/SIMD/VectorConvert.cs @@ -499,6 +499,7 @@ public static int VectorConvertUInt16And8(Vector A1, Vector A2) } [Fact] + [SkipOnMono("https://github.com/dotnet/runtime/issues/100368")] public static int TestEntryPoint() { int returnVal = Pass; From 5bddfc15923b968fe918d0f5a58612a0fb196c8d Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 13:51:47 -0700 Subject: [PATCH 14/16] Ensure that lowering on Arm64 doesn't make an assumption about cast shapes --- src/coreclr/jit/lowerarmarch.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6e3b8e3202991..3a32d9003e1a1 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1354,7 +1354,15 @@ bool Lowering::IsValidConstForMovImm(GenTreeHWIntrinsic* node) // can catch those cases as well. castOp = op1->AsCast()->CastOp(); - op1 = castOp; + + if (varTypeIsIntegral(castOp)) + { + op1 = castOp; + } + else + { + castOp = nullptr; + } } if (op1->IsCnsIntOrI()) From 1e0879f9ddcec7648d44414aa49833cf64cb540e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 18:52:10 -0700 Subject: [PATCH 15/16] Ensure the tier0opts local is used --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 910dec90a8cdb..c8cc9be9252c9 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3120,7 +3120,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // To be fixed in https://github.com/dotnet/runtime/pull/77465 const bool tier0opts = !opts.compDbgCode && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_MIN_OPT); - if (!mustExpand) + if (!mustExpand && tier0opts) { switch (ni) { From f63c575d2104208cc7bac53556444c5f7e8061a5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 May 2024 21:35:43 -0700 Subject: [PATCH 16/16] Ensure impEstimateIntrinsic bails out for APIs that need to be implemented as user calls --- src/coreclr/jit/importercalls.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index c8cc9be9252c9..4360779a577fe 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -7476,8 +7476,24 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) return false; } #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // TODO-LoongArch64: add some intrinsics. - return false; + switch (intrinsicName) + { + case NI_System_Math_Abs: + case NI_System_Math_Sqrt: + case NI_System_Math_ReciprocalSqrtEstimate: + { + // TODO-LoongArch64: support these standard intrinsics + // TODO-RISCV64: support these standard intrinsics + + return false; + } + + case NI_System_Math_ReciprocalEstimate: + return true; + + default: + return false; + } #else // TODO: This portion of logic is not implemented for other arch. // The reason for returning true is that on all other arch the only intrinsic @@ -8775,6 +8791,11 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, return nullptr; } + if (IsIntrinsicImplementedByUserCall(intrinsicName)) + { + return nullptr; + } + #if defined(FEATURE_HW_INTRINSICS) // We use compExactlyDependsOn since these are estimate APIs where // the behavior is explicitly allowed to differ across machines @@ -8868,6 +8889,7 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, if (intrinsicName == NI_System_Math_ReciprocalSqrtEstimate) { + assert(!IsIntrinsicImplementedByUserCall(NI_System_Math_Sqrt)); op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, nullptr); }