diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index 6b15c16981c93..2fc93df7e8ee6 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 080274dfb9d1b..3d0089d00eb11 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, @@ -8244,6 +8248,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..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); + 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 84b7e6b31387d..f0a941f6b28c7 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -337,6 +337,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 @@ -347,7 +348,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; @@ -626,10 +628,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); @@ -639,10 +649,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); @@ -663,10 +681,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); @@ -676,10 +702,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 19062dde2d7b7..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,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 943c75d2639bf..4360779a577fe 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 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()) diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index c9b227440d4e5..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); @@ -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 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.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 81a6792b599cd..6e6263f039049 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 521abb22465e7..0231d3e4ee720 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 || 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 05b404abccc51..31e7490602266 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 0e968e6fca8f3..8a5961bb314b0 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 04c5e0672a9d1..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 @@ -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 fe96bb1b419f7..bf7c06a935dfb 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 95cf6d16b6617..d01939ec548f9 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 a5559d43c8150..d3698db4e5b23 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 c01f88facbf57..f4cbb180fce9f 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 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))); } } } 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 + + + + + diff --git a/src/tests/JIT/SIMD/VectorConvert.cs b/src/tests/JIT/SIMD/VectorConvert.cs index 9bd57b9400898..6c1c4628bbb64 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