From ae11fbbb6de80fc6b22f09ae38c7881f59be7f9a Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 13 May 2024 14:01:21 -0700 Subject: [PATCH] Change the ReciprocalEstimate and ReciprocalSqrtEstimate APIs to be mustExpand on RyuJIT (#102098) * Change the ReciprocalEstimate and ReciprocalSqrtEstimate APIs to be mustExpand on RyuJIT * Apply formatting patch * Fix the RV64 and LA64 builds * Mark the ReciprocalEstimate and ReciprocalSqrtEstimate methods as AggressiveOptimization to bypass R2R * Mark other usages of ReciprocalEstimate and ReciprocalSqrtEstimate in Corelib with AggressiveOptimization * Mark several non-deterministic APIs as BypassReadyToRun and skip intrinsic expansion in R2R * Cleanup based on PR recommendations to rely on the runtime rather than attributation of non-deterministic intrinsics * Adding a regression test ensuring direct and indirect invocation of non-deterministic intrinsic APIs returns the same result * Add a note about non-deterministic intrinsic expansion to the botr * Apply formatting patch * Ensure vector tests are correctly validating against the scalar implementation * Fix the JIT/SIMD/VectorConvert test and workaround a 32-bit test issue * Skip a test on Mono due to a known/tracked issue * Ensure that lowering on Arm64 doesn't make an assumption about cast shapes * Ensure the tier0opts local is used * Ensure impEstimateIntrinsic bails out for APIs that need to be implemented as user calls --- .../coreclr/botr/vectors-and-intrinsics.md | 8 ++ src/coreclr/jit/compiler.h | 30 ++++- src/coreclr/jit/hwintrinsic.cpp | 3 +- src/coreclr/jit/hwintrinsicarm64.cpp | 52 ++++++-- src/coreclr/jit/hwintrinsicxarch.cpp | 42 +++++-- src/coreclr/jit/importercalls.cpp | 110 ++++++++++++----- src/coreclr/jit/lowerarmarch.cpp | 10 +- src/coreclr/jit/simdashwintrinsic.cpp | 40 +++++- .../tests/GenericVectorTests.cs | 8 +- .../src/System/Double.cs | 14 ++- .../System.Private.CoreLib/src/System/Math.cs | 10 +- .../src/System/MathF.cs | 10 +- .../src/System/Numerics/Vector.cs | 60 +++++++-- .../src/System/Runtime/Intrinsics/Vector64.cs | 60 +++++++-- .../src/System/Single.cs | 14 ++- .../tests/Vectors/Vector128Tests.cs | 79 +++++------- .../tests/Vectors/Vector256Tests.cs | 79 +++++------- .../tests/Vectors/Vector512Tests.cs | 79 +++++------- .../tests/Vectors/Vector64Tests.cs | 48 ++++---- .../JitBlue/Runtime_101731/Runtime_101731.cs | 116 ++++++++++++++++++ .../Runtime_101731/Runtime_101731.csproj | 8 ++ src/tests/JIT/SIMD/VectorConvert.cs | 43 ++++--- 22 files changed, 639 insertions(+), 284 deletions(-) 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/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index 6b15c16981c93f..2fc93df7e8ee68 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 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. + +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)`. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 01225550850ffb..ff4e21444894fa 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, @@ -8272,6 +8276,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 0a43bddbd34b19..e6b0e5fa72ffb1 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1577,7 +1577,8 @@ 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 e11f8751777081..ce3d6e5e69fa2d 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -473,6 +473,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 @@ -483,7 +484,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; @@ -762,10 +764,18 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToInt32: case NI_Vector64_ConvertToInt32Native: - case NI_Vector128_ConvertToInt32: case NI_Vector128_ConvertToInt32Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToInt32: + case NI_Vector128_ConvertToInt32: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); @@ -775,10 +785,18 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToInt64: case NI_Vector64_ConvertToInt64Native: - case NI_Vector128_ConvertToInt64: case NI_Vector128_ConvertToInt64Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToInt64: + case NI_Vector128_ConvertToInt64: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); @@ -799,10 +817,18 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToUInt32: case NI_Vector64_ConvertToUInt32Native: - case NI_Vector128_ConvertToUInt32: case NI_Vector128_ConvertToUInt32Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + FALLTHROUGH; + } + + case NI_Vector64_ConvertToUInt32: + case NI_Vector128_ConvertToUInt32: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); @@ -812,10 +838,18 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ConvertToUInt64: case NI_Vector64_ConvertToUInt64Native: - case NI_Vector128_ConvertToUInt64: case NI_Vector128_ConvertToUInt64Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + 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 524b54c00616d3..603d98cef20ecf 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,6 +1439,11 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNativeNode(retType, op1, CORINFO_TYPE_INT, simdBaseJitType, simdSize); break; @@ -1463,6 +1471,11 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1542,6 +1555,11 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1556,6 +1574,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); @@ -1571,6 +1590,11 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); assert(simdBaseType == TYP_DOUBLE); + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + break; + } + if (IsBaselineVector512IsaSupportedOpportunistically()) { op1 = impSIMDPopStack(); diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 943c75d2639bf1..4360779a577fe1 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); } } } @@ -3125,15 +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 (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 && tier0opts) { switch (ni) { @@ -4157,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; } @@ -5166,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 @@ -5174,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); @@ -5199,8 +5188,16 @@ GenTree* Compiler::impPrimitiveNamedIntrinsic(NamedIntrinsic intrinsic, switch (intrinsic) { - case NI_PRIMITIVE_ConvertToInteger: case NI_PRIMITIVE_ConvertToIntegerNative: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + return nullptr; + } + FALLTHROUGH; + } + + case NI_PRIMITIVE_ConvertToInteger: { assert(sig->sigInst.methInstCount == 1); assert(varTypeIsFloating(baseType)); @@ -5525,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) { @@ -7471,14 +7468,32 @@ 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: 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 @@ -8758,23 +8773,32 @@ 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 (BlockNonDeterministicIntrinsics(mustExpand)) + { + 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 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; @@ -8847,7 +8871,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 +8880,28 @@ 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) + { + assert(!IsIntrinsicImplementedByUserCall(NI_System_Math_Sqrt)); + 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 +9000,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 +9269,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 +9434,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/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6e3b8e3202991a..3a32d9003e1a15 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()) diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index c9b227440d4e5d..eadb82af79effd 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); @@ -512,8 +516,35 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, switch (intrinsic) { + case NI_VectorT_ConvertToInt32Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + return nullptr; + } + break; + } + + case NI_VectorT_ConvertToInt64Native: + case NI_VectorT_ConvertToUInt32Native: + case NI_VectorT_ConvertToUInt64Native: + { + if (BlockNonDeterministicIntrinsics(mustExpand)) + { + 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.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 367d2f96699640..97a65f0a84173e 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.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 81a6792b599cd5..6e6263f039049b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -663,7 +663,19 @@ public static TInteger ConvertToInteger(double value) /// [Intrinsic] 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 4bba6d5f832b8d..380b63e5416c86 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1198,10 +1198,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. @@ -1212,10 +1215,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 || TARGET_RISCV64 || TARGET_LOONGARCH64 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 05b404abccc51a..31e74906022666 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 || TARGET_RISCV64 || TARGET_LOONGARCH64 return 1.0f / Sqrt(x); +#else + return ReciprocalSqrtEstimate(x); +#endif } [Intrinsic] 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 0e968e6fca8f34..8a5961bb314b0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -335,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); } @@ -346,7 +346,18 @@ public static Vector ConvertToInt32(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - 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. @@ -358,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); } @@ -369,7 +380,18 @@ public static Vector ConvertToInt64(Vector value) /// The vector to convert. /// The converted vector. [Intrinsic] - 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. @@ -419,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); } @@ -431,7 +453,18 @@ public static Vector ConvertToUInt32(Vector value) /// The converted vector. [Intrinsic] [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. @@ -444,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); } @@ -456,7 +489,18 @@ public static Vector ConvertToUInt64(Vector value) /// The converted vector. [Intrinsic] [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/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 04c5e0672a9d1b..f3e35f7ecc36fc 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 @@ -350,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,7 +362,18 @@ public static unsafe Vector64 ConvertToInt32(Vector64 vector) /// The converted vector. [Intrinsic] [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. @@ -375,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); } @@ -387,7 +398,18 @@ public static unsafe Vector64 ConvertToInt64(Vector64 vector) /// The converted vector. [Intrinsic] [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. @@ -438,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); } @@ -451,7 +473,18 @@ public static unsafe Vector64 ConvertToUInt32(Vector64 vector) [Intrinsic] [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. @@ -465,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); } @@ -478,7 +511,18 @@ public static unsafe Vector64 ConvertToUInt64(Vector64 vector) [Intrinsic] [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 fe96bb1b419f7a..bf7c06a935dfb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -658,7 +658,19 @@ public static TInteger ConvertToInteger(float value) /// [Intrinsic] 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] diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 95cf6d16b66171..d01939ec548f97 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -4878,103 +4878,82 @@ 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(double.ConvertToIntegerNative(double.MinValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue))); + Assert.Equal(Vector128.Create(double.ConvertToIntegerNative(2.6)), Vector128.ConvertToInt64Native(Vector128.Create(2.6))); + + if (Environment.Is64BitProcess) { - Assert.Equal(Vector128.Create(long.MaxValue), Vector128.ConvertToInt64Native(Vector128.Create(double.MaxValue))); + // 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))); } - - Assert.Equal(Vector128.Create(long.MinValue), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue))); - Assert.Equal(Vector128.Create(2L), Vector128.ConvertToInt64Native(Vector128.Create(2.6))); } [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 + if (Environment.Is64BitProcess) { - Assert.Equal(Vector128.Create(ulong.MinValue), Vector128.ConvertToUInt64Native(Vector128.Create(double.MinValue))); + // 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(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(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 a5559d43c81505..d3698db4e5b23d 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -5893,103 +5893,82 @@ 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(double.ConvertToIntegerNative(double.MinValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue))); + Assert.Equal(Vector256.Create(double.ConvertToIntegerNative(2.6)), Vector256.ConvertToInt64Native(Vector256.Create(2.6))); + + if (Environment.Is64BitProcess) { - Assert.Equal(Vector256.Create(long.MaxValue), Vector256.ConvertToInt64Native(Vector256.Create(double.MaxValue))); + // 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))); } - - Assert.Equal(Vector256.Create(long.MinValue), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue))); - Assert.Equal(Vector256.Create(2L), Vector256.ConvertToInt64Native(Vector256.Create(2.6))); } [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 + if (Environment.Is64BitProcess) { - Assert.Equal(Vector256.Create(ulong.MinValue), Vector256.ConvertToUInt64Native(Vector256.Create(double.MinValue))); + // 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(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(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 c01f88facbf574..f4cbb180fce9f1 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5326,103 +5326,82 @@ 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(double.ConvertToIntegerNative(double.MinValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue))); + Assert.Equal(Vector512.Create(double.ConvertToIntegerNative(2.6)), Vector512.ConvertToInt64Native(Vector512.Create(2.6))); + + if (Environment.Is64BitProcess) { - Assert.Equal(Vector512.Create(long.MaxValue), Vector512.ConvertToInt64Native(Vector512.Create(double.MaxValue))); + // 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))); } - - Assert.Equal(Vector512.Create(long.MinValue), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue))); - Assert.Equal(Vector512.Create(2L), Vector512.ConvertToInt64Native(Vector512.Create(2.6))); } [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 + if (Environment.Is64BitProcess) { - Assert.Equal(Vector512.Create(ulong.MinValue), Vector512.ConvertToUInt64Native(Vector512.Create(double.MinValue))); + // 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(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(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 3c2d8064681fe1..18c1d3b05af17a 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))); } } } 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 00000000000000..98c9f2a570ab76 --- /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 00000000000000..de6d5e08882e86 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_101731/Runtime_101731.csproj @@ -0,0 +1,8 @@ + + + True + + + + + diff --git a/src/tests/JIT/SIMD/VectorConvert.cs b/src/tests/JIT/SIMD/VectorConvert.cs index 9bd57b94008983..6c1c4628bbb64f 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); @@ -495,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; @@ -508,7 +513,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector singleVector = getRandomVector(singles, i); @@ -518,7 +523,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector = getRandomVector(doubles, i); @@ -528,7 +533,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector = getRandomVector(doubles, i); @@ -538,7 +543,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector doubleVector1 = getRandomVector(doubles, i); @@ -549,7 +554,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int64Vector1 = getRandomVector(int64s, i); @@ -560,7 +565,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int32Vector1 = getRandomVector(int32s, i); @@ -571,7 +576,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector int16Vector1 = getRandomVector(int16s, i); @@ -582,7 +587,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint64Vector1 = getRandomVector(uint64s, i); @@ -593,7 +598,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint32Vector1 = getRandomVector(uint32s, i); @@ -604,7 +609,7 @@ public static int TestEntryPoint() returnVal = Fail; } } - + for (int i = 0; i < 10; i++) { Vector uint16Vector1 = getRandomVector(uint16s, i); @@ -616,9 +621,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