From 3bd417108a09d236dffccf54fe7ec58046feb5c4 Mon Sep 17 00:00:00 2001
From: Tanner Gooding <tagoo@outlook.com>
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<T> 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<T> 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<int> targetVec = Vector.ConvertToInt32(sourceVec);
             for (int i = 0; i < Vector<int>.Count; i++)
             {
-                Assert.Equal(unchecked((int)source[i]), targetVec[i]);
+                Assert.Equal(float.ConvertToInteger<int>(source[i]), targetVec[i]);
             }
         }
 
@@ -3176,7 +3176,7 @@ public void ConvertSingleToUInt32()
             Vector<uint> targetVec = Vector.ConvertToUInt32(sourceVec);
             for (int i = 0; i < Vector<uint>.Count; i++)
             {
-                Assert.Equal(unchecked((uint)source[i]), targetVec[i]);
+                Assert.Equal(float.ConvertToInteger<uint>(source[i]), targetVec[i]);
             }
         }
 
@@ -3188,7 +3188,7 @@ public void ConvertDoubleToInt64()
             Vector<long> targetVec = Vector.ConvertToInt64(sourceVec);
             for (int i = 0; i < Vector<long>.Count; i++)
             {
-                Assert.Equal(unchecked((long)source[i]), targetVec[i]);
+                Assert.Equal(double.ConvertToInteger<long>(source[i]), targetVec[i]);
             }
         }
 
@@ -3200,7 +3200,7 @@ public void ConvertDoubleToUInt64()
             Vector<ulong> targetVec = Vector.ConvertToUInt64(sourceVec);
             for (int i = 0; i < Vector<ulong>.Count; i++)
             {
-                Assert.Equal(unchecked((ulong)source[i]), targetVec[i]);
+                Assert.Equal(double.ConvertToInteger<ulong>(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<TInteger>(double value)
         /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToIntegerNative{TInteger}(TSelf)" />
         [Intrinsic]
         public static TInteger ConvertToIntegerNative<TInteger>(double value)
-            where TInteger : IBinaryInteger<TInteger> => TInteger.CreateSaturating(value);
+            where TInteger : IBinaryInteger<TInteger>
+        {
+#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<TInteger>(value);
+            }
+#endif
+
+            return TInteger.CreateSaturating(value);
+        }
 
         /// <inheritdoc cref="IFloatingPoint{TSelf}.Floor(TSelf)" />
         [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)
         ///    <para>On hardware without specialized support, this may just return <c>1.0 / d</c>.</para>
         /// </remarks>
         [Intrinsic]
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static double ReciprocalEstimate(double d)
         {
+#if MONO
             return 1.0 / d;
+#else
+            return ReciprocalEstimate(d);
+#endif
         }
 
         /// <summary>Returns an estimate of the reciprocal square root of a specified number.</summary>
@@ -1212,10 +1215,13 @@ public static double ReciprocalEstimate(double d)
         ///    <para>On hardware without specialized support, this may just return <c>1.0 / Sqrt(d)</c>.</para>
         /// </remarks>
         [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)
         ///    <para>On hardware without specialized support, this may just return <c>1.0 / x</c>.</para>
         /// </remarks>
         [Intrinsic]
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static float ReciprocalEstimate(float x)
         {
+#if MONO
             return 1.0f / x;
+#else
+            return ReciprocalEstimate(x);
+#endif
         }
 
         /// <summary>Returns an estimate of the reciprocal square root of a specified number.</summary>
@@ -329,10 +332,13 @@ public static float ReciprocalEstimate(float x)
         ///    <para>On hardware without specialized support, this may just return <c>1.0 / Sqrt(x)</c>.</para>
         /// </remarks>
         [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<int> ConvertToInt32(Vector<float> value)
 
             for (int i = 0; i < Vector<int>.Count; i++)
             {
-                int element = (int)value.GetElementUnsafe(i);
+                int element = float.ConvertToInteger<int>(value.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, element);
             }
 
@@ -346,7 +346,18 @@ public static Vector<int> ConvertToInt32(Vector<float> value)
         /// <param name="value">The vector to convert.</param>
         /// <returns>The converted vector.</returns>
         [Intrinsic]
-        public static Vector<int> ConvertToInt32Native(Vector<float> value) => ConvertToInt32(value);
+        public static Vector<int> ConvertToInt32Native(Vector<float> value)
+        {
+            Unsafe.SkipInit(out Vector<int> result);
+
+            for (int i = 0; i < Vector<int>.Count; i++)
+            {
+                int element = float.ConvertToIntegerNative<int>(value.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, element);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector{Double}" /> to a <see cref="Vector{Int64}" /> using saturation on overflow.</summary>
         /// <param name="value">The vector to convert.</param>
@@ -358,7 +369,7 @@ public static Vector<long> ConvertToInt64(Vector<double> value)
 
             for (int i = 0; i < Vector<long>.Count; i++)
             {
-                long element = (long)value.GetElementUnsafe(i);
+                long element = double.ConvertToInteger<long>(value.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, element);
             }
 
@@ -369,7 +380,18 @@ public static Vector<long> ConvertToInt64(Vector<double> value)
         /// <param name="value">The vector to convert.</param>
         /// <returns>The converted vector.</returns>
         [Intrinsic]
-        public static Vector<long> ConvertToInt64Native(Vector<double> value) => ConvertToInt64(value);
+        public static Vector<long> ConvertToInt64Native(Vector<double> value)
+        {
+            Unsafe.SkipInit(out Vector<long> result);
+
+            for (int i = 0; i < Vector<long>.Count; i++)
+            {
+                long element = double.ConvertToIntegerNative<long>(value.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, element);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector{Int32}" /> to a <see cref="Vector{Single}" />.</summary>
         /// <param name="value">The vector to convert.</param>
@@ -419,7 +441,7 @@ public static Vector<uint> ConvertToUInt32(Vector<float> value)
 
             for (int i = 0; i < Vector<uint>.Count; i++)
             {
-                uint element = (uint)value.GetElementUnsafe(i);
+                uint element = float.ConvertToInteger<uint>(value.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, element);
             }
 
@@ -431,7 +453,18 @@ public static Vector<uint> ConvertToUInt32(Vector<float> value)
         /// <returns>The converted vector.</returns>
         [Intrinsic]
         [CLSCompliant(false)]
-        public static Vector<uint> ConvertToUInt32Native(Vector<float> value) => ConvertToUInt32(value);
+        public static Vector<uint> ConvertToUInt32Native(Vector<float> value)
+        {
+            Unsafe.SkipInit(out Vector<uint> result);
+
+            for (int i = 0; i < Vector<uint>.Count; i++)
+            {
+                uint element = float.ConvertToIntegerNative<uint>(value.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, element);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector{Double}" /> to a <see cref="Vector{UInt64}" /> using saturation on overflow.</summary>
         /// <param name="value">The vector to convert.</param>
@@ -444,7 +477,7 @@ public static Vector<ulong> ConvertToUInt64(Vector<double> value)
 
             for (int i = 0; i < Vector<ulong>.Count; i++)
             {
-                ulong element = (ulong)value.GetElementUnsafe(i);
+                ulong element = double.ConvertToInteger<ulong>(value.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, element);
             }
 
@@ -456,7 +489,18 @@ public static Vector<ulong> ConvertToUInt64(Vector<double> value)
         /// <returns>The converted vector.</returns>
         [Intrinsic]
         [CLSCompliant(false)]
-        public static Vector<ulong> ConvertToUInt64Native(Vector<double> value) => ConvertToUInt64(value);
+        public static Vector<ulong> ConvertToUInt64Native(Vector<double> value)
+        {
+            Unsafe.SkipInit(out Vector<ulong> result);
+
+            for (int i = 0; i < Vector<ulong>.Count; i++)
+            {
+                ulong element = double.ConvertToIntegerNative<ulong>(value.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, element);
+            }
+
+            return result;
+        }
 
         /// <summary>Creates a new <see cref="Vector{T}" /> instance where the elements begin at a specified value and which are spaced apart according to another specified value.</summary>
         /// <typeparam name="T">The type of the elements in the vector.</typeparam>
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<int> ConvertToInt32(Vector64<float> vector)
 
             for (int i = 0; i < Vector64<int>.Count; i++)
             {
-                int value = (int)vector.GetElementUnsafe(i);
+                int value = float.ConvertToInteger<int>(vector.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, value);
             }
 
@@ -362,7 +362,18 @@ public static unsafe Vector64<int> ConvertToInt32(Vector64<float> vector)
         /// <returns>The converted vector.</returns>
         [Intrinsic]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static unsafe Vector64<int> ConvertToInt32Native(Vector64<float> vector) => ConvertToInt32(vector);
+        public static unsafe Vector64<int> ConvertToInt32Native(Vector64<float> vector)
+        {
+            Unsafe.SkipInit(out Vector64<int> result);
+
+            for (int i = 0; i < Vector64<int>.Count; i++)
+            {
+                int value = float.ConvertToIntegerNative<int>(vector.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, value);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector64{Double}" /> to a <see cref="Vector64{Int64}" /> using saturation on overflow.</summary>
         /// <param name="vector">The vector to convert.</param>
@@ -375,7 +386,7 @@ public static unsafe Vector64<long> ConvertToInt64(Vector64<double> vector)
 
             for (int i = 0; i < Vector64<long>.Count; i++)
             {
-                long value = (long)vector.GetElementUnsafe(i);
+                long value = double.ConvertToInteger<long>(vector.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, value);
             }
 
@@ -387,7 +398,18 @@ public static unsafe Vector64<long> ConvertToInt64(Vector64<double> vector)
         /// <returns>The converted vector.</returns>
         [Intrinsic]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static unsafe Vector64<long> ConvertToInt64Native(Vector64<double> vector) => ConvertToInt64(vector);
+        public static unsafe Vector64<long> ConvertToInt64Native(Vector64<double> vector)
+        {
+            Unsafe.SkipInit(out Vector64<long> result);
+
+            for (int i = 0; i < Vector64<long>.Count; i++)
+            {
+                long value = double.ConvertToIntegerNative<long>(vector.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, value);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector64{Int32}" /> to a <see cref="Vector64{Single}" />.</summary>
         /// <param name="vector">The vector to convert.</param>
@@ -438,7 +460,7 @@ public static unsafe Vector64<uint> ConvertToUInt32(Vector64<float> vector)
 
             for (int i = 0; i < Vector64<uint>.Count; i++)
             {
-                uint value = (uint)vector.GetElementUnsafe(i);
+                uint value = float.ConvertToInteger<uint>(vector.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, value);
             }
 
@@ -451,7 +473,18 @@ public static unsafe Vector64<uint> ConvertToUInt32(Vector64<float> vector)
         [Intrinsic]
         [CLSCompliant(false)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static unsafe Vector64<uint> ConvertToUInt32Native(Vector64<float> vector) => ConvertToUInt32(vector);
+        public static unsafe Vector64<uint> ConvertToUInt32Native(Vector64<float> vector)
+        {
+            Unsafe.SkipInit(out Vector64<uint> result);
+
+            for (int i = 0; i < Vector64<uint>.Count; i++)
+            {
+                uint value = float.ConvertToIntegerNative<uint>(vector.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, value);
+            }
+
+            return result;
+        }
 
         /// <summary>Converts a <see cref="Vector64{Double}" /> to a <see cref="Vector64{UInt64}" /> using saturation on overflow.</summary>
         /// <param name="vector">The vector to convert.</param>
@@ -465,7 +498,7 @@ public static unsafe Vector64<ulong> ConvertToUInt64(Vector64<double> vector)
 
             for (int i = 0; i < Vector64<ulong>.Count; i++)
             {
-                ulong value = (ulong)vector.GetElementUnsafe(i);
+                ulong value = double.ConvertToInteger<ulong>(vector.GetElementUnsafe(i));
                 result.SetElementUnsafe(i, value);
             }
 
@@ -478,7 +511,18 @@ public static unsafe Vector64<ulong> ConvertToUInt64(Vector64<double> vector)
         [Intrinsic]
         [CLSCompliant(false)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static unsafe Vector64<ulong> ConvertToUInt64Native(Vector64<double> vector) => ConvertToUInt64(vector);
+        public static unsafe Vector64<ulong> ConvertToUInt64Native(Vector64<double> vector)
+        {
+            Unsafe.SkipInit(out Vector64<ulong> result);
+
+            for (int i = 0; i < Vector64<ulong>.Count; i++)
+            {
+                ulong value = double.ConvertToIntegerNative<ulong>(vector.GetElementUnsafe(i));
+                result.SetElementUnsafe(i, value);
+            }
+
+            return result;
+        }
 
         /// <summary>Copies a <see cref="Vector64{T}" /> to a given array.</summary>
         /// <typeparam name="T">The type of the elements in the vector.</typeparam>
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<TInteger>(float value)
         /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToIntegerNative{TInteger}(TSelf)" />
         [Intrinsic]
         public static TInteger ConvertToIntegerNative<TInteger>(float value)
-            where TInteger : IBinaryInteger<TInteger> => TInteger.CreateSaturating(value);
+            where TInteger : IBinaryInteger<TInteger>
+        {
+#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<TInteger>(value);
+            }
+#endif
+
+            return TInteger.CreateSaturating(value);
+        }
 
         /// <inheritdoc cref="IFloatingPoint{TSelf}.Floor(TSelf)" />
         [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<int>(float.MinValue)), Vector128.ConvertToInt32(Vector128.Create(float.MinValue)));
+            Assert.Equal(Vector128.Create(float.ConvertToInteger<int>(2.6f)), Vector128.ConvertToInt32(Vector128.Create(2.6f)));
+            Assert.Equal(Vector128.Create(float.ConvertToInteger<int>(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<int>(float.MinValue)), Vector128.ConvertToInt32Native(Vector128.Create(float.MinValue)));
+            Assert.Equal(Vector128.Create(float.ConvertToIntegerNative<int>(2.6f)), Vector128.ConvertToInt32Native(Vector128.Create(2.6f)));
+            Assert.Equal(Vector128.Create(float.ConvertToIntegerNative<int>(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<long>(double.MinValue)), Vector128.ConvertToInt64(Vector128.Create(double.MinValue)));
+            Assert.Equal(Vector128.Create(double.ConvertToInteger<long>(2.6)), Vector128.ConvertToInt64(Vector128.Create(2.6)));
+            Assert.Equal(Vector128.Create(double.ConvertToInteger<long>(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<long>(double.MinValue)), Vector128.ConvertToInt64Native(Vector128.Create(double.MinValue)));
+            Assert.Equal(Vector128.Create(double.ConvertToIntegerNative<long>(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<long>(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<uint>(float.MinValue)), Vector128.ConvertToUInt32(Vector128.Create(float.MinValue)));
+            Assert.Equal(Vector128.Create(float.ConvertToInteger<uint>(2.6f)), Vector128.ConvertToUInt32(Vector128.Create(2.6f)));
+            Assert.Equal(Vector128.Create(float.ConvertToInteger<uint>(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<uint>(float.MinValue)), Vector128.ConvertToUInt32Native(Vector128.Create(float.MinValue)));
+            Assert.Equal(Vector128.Create(float.ConvertToIntegerNative<uint>(2.6f)), Vector128.ConvertToUInt32Native(Vector128.Create(2.6f)));
+            Assert.Equal(Vector128.Create(float.ConvertToIntegerNative<uint>(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<ulong>(double.MinValue)), Vector128.ConvertToUInt64(Vector128.Create(double.MinValue)));
+            Assert.Equal(Vector128.Create(double.ConvertToInteger<ulong>(2.6)), Vector128.ConvertToUInt64(Vector128.Create(2.6)));
+            Assert.Equal(Vector128.Create(double.ConvertToInteger<ulong>(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<ulong>(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<ulong>(2.6)), Vector128.ConvertToUInt64Native(Vector128.Create(2.6)));
+            Assert.Equal(Vector128.Create(double.ConvertToIntegerNative<ulong>(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<int>(float.MinValue)), Vector256.ConvertToInt32(Vector256.Create(float.MinValue)));
+            Assert.Equal(Vector256.Create(float.ConvertToInteger<int>(2.6f)), Vector256.ConvertToInt32(Vector256.Create(2.6f)));
+            Assert.Equal(Vector256.Create(float.ConvertToInteger<int>(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<int>(float.MinValue)), Vector256.ConvertToInt32Native(Vector256.Create(float.MinValue)));
+            Assert.Equal(Vector256.Create(float.ConvertToIntegerNative<int>(2.6f)), Vector256.ConvertToInt32Native(Vector256.Create(2.6f)));
+            Assert.Equal(Vector256.Create(float.ConvertToIntegerNative<int>(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<long>(double.MinValue)), Vector256.ConvertToInt64(Vector256.Create(double.MinValue)));
+            Assert.Equal(Vector256.Create(double.ConvertToInteger<long>(2.6)), Vector256.ConvertToInt64(Vector256.Create(2.6)));
+            Assert.Equal(Vector256.Create(double.ConvertToInteger<long>(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<long>(double.MinValue)), Vector256.ConvertToInt64Native(Vector256.Create(double.MinValue)));
+            Assert.Equal(Vector256.Create(double.ConvertToIntegerNative<long>(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<long>(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<uint>(float.MinValue)), Vector256.ConvertToUInt32(Vector256.Create(float.MinValue)));
+            Assert.Equal(Vector256.Create(float.ConvertToInteger<uint>(2.6f)), Vector256.ConvertToUInt32(Vector256.Create(2.6f)));
+            Assert.Equal(Vector256.Create(float.ConvertToInteger<uint>(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<uint>(float.MinValue)), Vector256.ConvertToUInt32Native(Vector256.Create(float.MinValue)));
+            Assert.Equal(Vector256.Create(float.ConvertToIntegerNative<uint>(2.6f)), Vector256.ConvertToUInt32Native(Vector256.Create(2.6f)));
+            Assert.Equal(Vector256.Create(float.ConvertToIntegerNative<uint>(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<ulong>(double.MinValue)), Vector256.ConvertToUInt64(Vector256.Create(double.MinValue)));
+            Assert.Equal(Vector256.Create(double.ConvertToInteger<ulong>(2.6)), Vector256.ConvertToUInt64(Vector256.Create(2.6)));
+            Assert.Equal(Vector256.Create(double.ConvertToInteger<ulong>(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<ulong>(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<ulong>(2.6)), Vector256.ConvertToUInt64Native(Vector256.Create(2.6)));
+            Assert.Equal(Vector256.Create(double.ConvertToIntegerNative<ulong>(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<int>(float.MinValue)), Vector512.ConvertToInt32(Vector512.Create(float.MinValue)));
+            Assert.Equal(Vector512.Create(float.ConvertToInteger<int>(2.6f)), Vector512.ConvertToInt32(Vector512.Create(2.6f)));
+            Assert.Equal(Vector512.Create(float.ConvertToInteger<int>(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<int>(float.MinValue)), Vector512.ConvertToInt32Native(Vector512.Create(float.MinValue)));
+            Assert.Equal(Vector512.Create(float.ConvertToIntegerNative<int>(2.6f)), Vector512.ConvertToInt32Native(Vector512.Create(2.6f)));
+            Assert.Equal(Vector512.Create(float.ConvertToIntegerNative<int>(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<long>(double.MinValue)), Vector512.ConvertToInt64(Vector512.Create(double.MinValue)));
+            Assert.Equal(Vector512.Create(double.ConvertToInteger<long>(2.6)), Vector512.ConvertToInt64(Vector512.Create(2.6)));
+            Assert.Equal(Vector512.Create(double.ConvertToInteger<long>(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<long>(double.MinValue)), Vector512.ConvertToInt64Native(Vector512.Create(double.MinValue)));
+            Assert.Equal(Vector512.Create(double.ConvertToIntegerNative<long>(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<long>(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<uint>(float.MinValue)), Vector512.ConvertToUInt32(Vector512.Create(float.MinValue)));
+            Assert.Equal(Vector512.Create(float.ConvertToInteger<uint>(2.6f)), Vector512.ConvertToUInt32(Vector512.Create(2.6f)));
+            Assert.Equal(Vector512.Create(float.ConvertToInteger<uint>(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<uint>(float.MinValue)), Vector512.ConvertToUInt32Native(Vector512.Create(float.MinValue)));
+            Assert.Equal(Vector512.Create(float.ConvertToIntegerNative<uint>(2.6f)), Vector512.ConvertToUInt32Native(Vector512.Create(2.6f)));
+            Assert.Equal(Vector512.Create(float.ConvertToIntegerNative<uint>(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<ulong>(double.MinValue)), Vector512.ConvertToUInt64(Vector512.Create(double.MinValue)));
+            Assert.Equal(Vector512.Create(double.ConvertToInteger<ulong>(2.6)), Vector512.ConvertToUInt64(Vector512.Create(2.6)));
+            Assert.Equal(Vector512.Create(double.ConvertToInteger<ulong>(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<ulong>(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<ulong>(2.6)), Vector512.ConvertToUInt64Native(Vector512.Create(2.6)));
+            Assert.Equal(Vector512.Create(double.ConvertToIntegerNative<ulong>(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<int>(float.MinValue)), Vector64.ConvertToInt32(Vector64.Create(float.MinValue)));
+            Assert.Equal(Vector64.Create(float.ConvertToInteger<int>(2.6f)), Vector64.ConvertToInt32(Vector64.Create(2.6f)));
+            Assert.Equal(Vector64.Create(float.ConvertToInteger<int>(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<int>(float.MinValue)), Vector64.ConvertToInt32Native(Vector64.Create(float.MinValue)));
+            Assert.Equal(Vector64.Create(float.ConvertToIntegerNative<int>(2.6f)), Vector64.ConvertToInt32Native(Vector64.Create(2.6f)));
+            Assert.Equal(Vector64.Create(float.ConvertToIntegerNative<int>(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<long>(double.MinValue)), Vector64.ConvertToInt64(Vector64.Create(double.MinValue)));
+            Assert.Equal(Vector64.Create(double.ConvertToInteger<long>(2.6)), Vector64.ConvertToInt64(Vector64.Create(2.6)));
+            Assert.Equal(Vector64.Create(double.ConvertToInteger<long>(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<long>(double.MinValue)), Vector64.ConvertToInt64Native(Vector64.Create(double.MinValue)));
+            Assert.Equal(Vector64.Create(double.ConvertToIntegerNative<long>(2.6)), Vector64.ConvertToInt64Native(Vector64.Create(2.6)));
+            Assert.Equal(Vector64.Create(double.ConvertToIntegerNative<long>(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<uint>(float.MinValue)), Vector64.ConvertToUInt32(Vector64.Create(float.MinValue)));
+            Assert.Equal(Vector64.Create(float.ConvertToInteger<uint>(2.6f)), Vector64.ConvertToUInt32(Vector64.Create(2.6f)));
+            Assert.Equal(Vector64.Create(float.ConvertToInteger<uint>(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<uint>(float.MinValue)), Vector64.ConvertToUInt32Native(Vector64.Create(float.MinValue)));
+            Assert.Equal(Vector64.Create(float.ConvertToIntegerNative<uint>(2.6f)), Vector64.ConvertToUInt32Native(Vector64.Create(2.6f)));
+            Assert.Equal(Vector64.Create(float.ConvertToIntegerNative<uint>(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<ulong>(double.MinValue)), Vector64.ConvertToUInt64(Vector64.Create(double.MinValue)));
+            Assert.Equal(Vector64.Create(double.ConvertToInteger<ulong>(2.6)), Vector64.ConvertToUInt64(Vector64.Create(2.6)));
+            Assert.Equal(Vector64.Create(double.ConvertToInteger<ulong>(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<ulong>(double.MinValue)), Vector64.ConvertToUInt64Native(Vector64.Create(double.MinValue)));
+            Assert.Equal(Vector64.Create(double.ConvertToIntegerNative<ulong>(2.6)), Vector64.ConvertToUInt64Native(Vector64.Create(2.6)));
+            Assert.Equal(Vector64.Create(double.ConvertToIntegerNative<ulong>(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<double, int> func = double.ConvertToIntegerNative<int>;
+        int expectedValue = double.ConvertToIntegerNative<int>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(float.MaxValue)]
+    public static void TestConvertToInt32NativeSingle(float value)
+    {
+        Func<float, int> func = float.ConvertToIntegerNative<int>;
+        int expectedValue = float.ConvertToIntegerNative<int>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(double.MaxValue)]
+    public static void TestConvertToInt64NativeDouble(double value)
+    {
+        Func<double, long> func = double.ConvertToIntegerNative<long>;
+        long expectedValue = double.ConvertToIntegerNative<long>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(float.MaxValue)]
+    public static void TestConvertToInt64NativeSingle(float value)
+    {
+        Func<float, long> func = float.ConvertToIntegerNative<long>;
+        long expectedValue = float.ConvertToIntegerNative<long>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(double.MaxValue)]
+    public static void TestConvertToUInt32NativeDouble(double value)
+    {
+        Func<double, uint> func = double.ConvertToIntegerNative<uint>;
+        uint expectedValue = double.ConvertToIntegerNative<uint>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(float.MaxValue)]
+    public static void TestConvertToUInt32NativeSingle(float value)
+    {
+        Func<float, uint> func = float.ConvertToIntegerNative<uint>;
+        uint expectedValue = float.ConvertToIntegerNative<uint>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(double.MaxValue)]
+    public static void TestConvertToUInt64NativeDouble(double value)
+    {
+        Func<double, ulong> func = double.ConvertToIntegerNative<ulong>;
+        ulong expectedValue = double.ConvertToIntegerNative<ulong>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(float.MaxValue)]
+    public static void TestConvertToUInt64NativeSingle(float value)
+    {
+        Func<float, ulong> func = float.ConvertToIntegerNative<ulong>;
+        ulong expectedValue = float.ConvertToIntegerNative<ulong>(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(5)]
+    public static void TestReciprocalEstimateDouble(double value)
+    {
+        Func<double, double> func = double.ReciprocalEstimate;
+        double expectedValue = double.ReciprocalEstimate(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(5)]
+    public static void TestReciprocalEstimateSingle(float value)
+    {
+        Func<float, float> func = float.ReciprocalEstimate;
+        float expectedValue = float.ReciprocalEstimate(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(-double.Epsilon)]
+    public static void TestReciprocalSqrtEstimateDouble(double value)
+    {
+        Func<double, double> func = double.ReciprocalSqrtEstimate;
+        double expectedValue = double.ReciprocalSqrtEstimate(value);
+        Assert.Equal(expectedValue, func(value));
+    }
+
+    [Theory]
+    [InlineData(-float.Epsilon)]
+    public static void TestReciprocalSqrtEstimateSingle(float value)
+    {
+        Func<float, float> 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 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
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<Single> A)
             int returnVal = Pass;
             for (int i = 0; i < Vector<Single>.Count; i++)
             {
-                Int32 int32Val = (Int32)A[i];
+                Int32 int32Val = float.ConvertToInteger<int>(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<Single> A)
             int returnVal = Pass;
             for (int i = 0; i < Vector<Single>.Count; i++)
             {
-                UInt32 uint32Val = (UInt32)A[i];
+                UInt32 uint32Val = float.ConvertToInteger<uint>(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<Double> A)
             int returnVal = Pass;
             for (int i = 0; i < Vector<Double>.Count; i++)
             {
-                Int64 int64Val = (Int64)A[i];
+                Int64 int64Val = double.ConvertToInteger<long>(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<Double> A)
             int returnVal = Pass;
             for (int i = 0; i < Vector<Double>.Count; i++)
             {
-                UInt64 uint64Val = (UInt64)A[i];
+                UInt64 uint64Val = double.ConvertToInteger<ulong>(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<Int32> A1, Vector<Int32> A2)
             }
             return returnVal;
         }
-        
+
         public static int VectorConvertInt16And8(Vector<Int16> A1, Vector<Int16> A2)
         {
             Vector<SByte> B = Vector.Narrow(A1, A2);
@@ -378,7 +382,7 @@ public static int VectorConvertInt16And8(Vector<Int16> A1, Vector<Int16> A2)
             }
             return returnVal;
         }
-        
+
         public static int VectorConvertUInt64And32(Vector<UInt64> A1, Vector<UInt64> A2)
         {
             Vector<UInt32> B = Vector.Narrow(A1, A2);
@@ -454,7 +458,7 @@ public static int VectorConvertUInt32And16(Vector<UInt32> A1, Vector<UInt32> A2)
             }
             return returnVal;
         }
-        
+
         public static int VectorConvertUInt16And8(Vector<UInt16> A1, Vector<UInt16> A2)
         {
             Vector<Byte> B = Vector.Narrow(A1, A2);
@@ -495,6 +499,7 @@ public static int VectorConvertUInt16And8(Vector<UInt16> A1, Vector<UInt16> 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<Single> singleVector = getRandomVector<Single>(singles, i);
@@ -518,7 +523,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Double> doubleVector = getRandomVector<Double>(doubles, i);
@@ -528,7 +533,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Double> doubleVector = getRandomVector<Double>(doubles, i);
@@ -538,7 +543,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Double> doubleVector1 = getRandomVector<Double>(doubles, i);
@@ -549,7 +554,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Int64> int64Vector1 = getRandomVector<Int64>(int64s, i);
@@ -560,7 +565,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Int32> int32Vector1 = getRandomVector<Int32>(int32s, i);
@@ -571,7 +576,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<Int16> int16Vector1 = getRandomVector<Int16>(int16s, i);
@@ -582,7 +587,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<UInt64> uint64Vector1 = getRandomVector<UInt64>(uint64s, i);
@@ -593,7 +598,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<UInt32> uint32Vector1 = getRandomVector<UInt32>(uint32s, i);
@@ -604,7 +609,7 @@ public static int TestEntryPoint()
                 returnVal = Fail;
             }
         }
-        
+
         for (int i = 0; i < 10; i++)
         {
             Vector<UInt16> uint16Vector1 = getRandomVector<UInt16>(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