@@ -3125,7 +3125,15 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
3125
3125
// To be fixed in https://github.com/dotnet/runtime/pull/77465
3126
3126
const bool tier0opts = !opts.compDbgCode && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_MIN_OPT);
3127
3127
3128
- if (!mustExpand && tier0opts)
3128
+ if (tier0opts)
3129
+ {
3130
+ // The *Estimate APIs are allowed to differ in behavior across hardware
3131
+ // so ensure we treat them as "betterToExpand" to get deterministic behavior
3132
+
3133
+ betterToExpand |= (ni == NI_System_Math_ReciprocalEstimate);
3134
+ betterToExpand |= (ni == NI_System_Math_ReciprocalSqrtEstimate);
3135
+ }
3136
+ else if (!mustExpand)
3129
3137
{
3130
3138
switch (ni)
3131
3139
{
@@ -3189,9 +3197,9 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
3189
3197
break;
3190
3198
3191
3199
default:
3192
- // Unsafe.* are all small enough to prefer expansions.
3200
+ // Various intrinsics are all small enough to prefer expansions.
3201
+ betterToExpand |= ni >= NI_SYSTEM_MATH_START && ni <= NI_SYSTEM_MATH_END;
3193
3202
betterToExpand |= ni >= NI_SRCS_UNSAFE_START && ni <= NI_SRCS_UNSAFE_END;
3194
- // Same for these
3195
3203
betterToExpand |= ni >= NI_PRIMITIVE_START && ni <= NI_PRIMITIVE_END;
3196
3204
break;
3197
3205
}
@@ -4146,6 +4154,13 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
4146
4154
break;
4147
4155
}
4148
4156
4157
+ case NI_System_Math_ReciprocalEstimate:
4158
+ case NI_System_Math_ReciprocalSqrtEstimate:
4159
+ {
4160
+ retNode = impEstimateIntrinsic(method, sig, callJitType, ni, tailCall);
4161
+ break;
4162
+ }
4163
+
4149
4164
case NI_System_Array_Clone:
4150
4165
case NI_System_Collections_Generic_Comparer_get_Default:
4151
4166
case NI_System_Collections_Generic_EqualityComparer_get_Default:
@@ -7413,13 +7428,15 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName)
7413
7428
// instructions to directly compute round/ceiling/floor/truncate.
7414
7429
7415
7430
case NI_System_Math_Abs:
7431
+ case NI_System_Math_ReciprocalEstimate:
7432
+ case NI_System_Math_ReciprocalSqrtEstimate:
7416
7433
case NI_System_Math_Sqrt:
7417
7434
return true;
7418
7435
7419
7436
case NI_System_Math_Ceiling:
7420
7437
case NI_System_Math_Floor:
7421
- case NI_System_Math_Truncate:
7422
7438
case NI_System_Math_Round:
7439
+ case NI_System_Math_Truncate:
7423
7440
return compOpportunisticallyDependsOn(InstructionSet_SSE41);
7424
7441
7425
7442
case NI_System_Math_FusedMultiplyAdd:
@@ -7434,11 +7451,13 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName)
7434
7451
case NI_System_Math_Abs:
7435
7452
case NI_System_Math_Ceiling:
7436
7453
case NI_System_Math_Floor:
7437
- case NI_System_Math_Truncate:
7438
- case NI_System_Math_Round:
7439
- case NI_System_Math_Sqrt:
7440
7454
case NI_System_Math_Max:
7441
7455
case NI_System_Math_Min:
7456
+ case NI_System_Math_ReciprocalEstimate:
7457
+ case NI_System_Math_ReciprocalSqrtEstimate:
7458
+ case NI_System_Math_Round:
7459
+ case NI_System_Math_Sqrt:
7460
+ case NI_System_Math_Truncate:
7442
7461
return true;
7443
7462
7444
7463
case NI_System_Math_FusedMultiplyAdd:
@@ -7513,6 +7532,8 @@ bool Compiler::IsMathIntrinsic(NamedIntrinsic intrinsicName)
7513
7532
case NI_System_Math_MinMagnitudeNumber:
7514
7533
case NI_System_Math_MinNumber:
7515
7534
case NI_System_Math_Pow:
7535
+ case NI_System_Math_ReciprocalEstimate:
7536
+ case NI_System_Math_ReciprocalSqrtEstimate:
7516
7537
case NI_System_Math_Round:
7517
7538
case NI_System_Math_Sin:
7518
7539
case NI_System_Math_Sinh:
@@ -8728,6 +8749,119 @@ void Compiler::impCheckCanInline(GenTreeCall* call,
8728
8749
}
8729
8750
}
8730
8751
8752
+ //------------------------------------------------------------------------
8753
+ // impEstimateIntrinsic: Imports one of the *Estimate intrinsics which are
8754
+ // explicitly allowed to differ in result based on the hardware they're running
8755
+ // against
8756
+ //
8757
+ // Arguments:
8758
+ // method - The handle of the method being imported
8759
+ // callType - The underlying type for the call
8760
+ // intrinsicName - The intrinsic being imported
8761
+ // tailCall - true if the method is a tail call; otherwise false
8762
+ //
8763
+ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method,
8764
+ CORINFO_SIG_INFO* sig,
8765
+ CorInfoType callJitType,
8766
+ NamedIntrinsic intrinsicName,
8767
+ bool tailCall)
8768
+ {
8769
+ var_types callType = JITtype2varType(callJitType);
8770
+
8771
+ assert(varTypeIsFloating(callType));
8772
+ assert(sig->numArgs == 1);
8773
+
8774
+ #if defined(FEATURE_HW_INTRINSICS)
8775
+ // We use compExactlyDependsOn since these are estimate APIs where
8776
+ // the behavior is explicitly allowed to differ across machines and
8777
+ // we want to ensure that it gets marked as such in R2R.
8778
+
8779
+ var_types simdType = TYP_UNKNOWN;
8780
+ NamedIntrinsic intrinsicId = NI_Illegal;
8781
+
8782
+ switch (intrinsicName)
8783
+ {
8784
+ case NI_System_Math_ReciprocalEstimate:
8785
+ {
8786
+ #if defined(TARGET_XARCH)
8787
+ if (compExactlyDependsOn(InstructionSet_AVX512F))
8788
+ {
8789
+ simdType = TYP_SIMD16;
8790
+ intrinsicId = NI_AVX512F_Reciprocal14Scalar;
8791
+ }
8792
+ else if ((callType == TYP_FLOAT) && compExactlyDependsOn(InstructionSet_SSE))
8793
+ {
8794
+ simdType = TYP_SIMD16;
8795
+ intrinsicId = NI_SSE_ReciprocalScalar;
8796
+ }
8797
+ #elif defined(TARGET_ARM64)
8798
+ if (compExactlyDependsOn(InstructionSet_AdvSimd_Arm64))
8799
+ {
8800
+ simdType = TYP_SIMD8;
8801
+ intrinsicId = NI_AdvSimd_Arm64_ReciprocalEstimateScalar;
8802
+ }
8803
+ #endif // TARGET_ARM64
8804
+ break;
8805
+ }
8806
+
8807
+ case NI_System_Math_ReciprocalSqrtEstimate:
8808
+ {
8809
+ #if defined(TARGET_XARCH)
8810
+ if (compExactlyDependsOn(InstructionSet_AVX512F))
8811
+ {
8812
+ simdType = TYP_SIMD16;
8813
+ intrinsicId = NI_AVX512F_ReciprocalSqrt14Scalar;
8814
+ }
8815
+ else if ((callType == TYP_FLOAT) && compExactlyDependsOn(InstructionSet_SSE))
8816
+ {
8817
+ simdType = TYP_SIMD16;
8818
+ intrinsicId = NI_SSE_ReciprocalSqrtScalar;
8819
+ }
8820
+ #elif defined(TARGET_ARM64)
8821
+ if (compExactlyDependsOn(InstructionSet_AdvSimd_Arm64))
8822
+ {
8823
+ simdType = TYP_SIMD8;
8824
+ intrinsicId = NI_AdvSimd_Arm64_ReciprocalSquareRootEstimateScalar;
8825
+ }
8826
+ #endif // TARGET_ARM64
8827
+ break;
8828
+ }
8829
+
8830
+ default:
8831
+ {
8832
+ unreached();
8833
+ }
8834
+ }
8835
+
8836
+ if (intrinsicId != NI_Illegal)
8837
+ {
8838
+ unsigned simdSize = 0;
8839
+
8840
+ if (simdType == TYP_SIMD8)
8841
+ {
8842
+ simdSize = 8;
8843
+ }
8844
+ else
8845
+ {
8846
+ assert(simdType == TYP_SIMD16);
8847
+ simdSize = 16;
8848
+ }
8849
+
8850
+ GenTree* op1 = impPopStack().val;
8851
+
8852
+ op1 = gtNewSimdCreateScalarUnsafeNode(simdType, op1, callJitType, simdSize);
8853
+ op1 = gtNewSimdHWIntrinsicNode(simdType, op1, intrinsicId, callJitType, simdSize);
8854
+
8855
+ return gtNewSimdToScalarNode(callType, op1, callJitType, simdSize);
8856
+ }
8857
+ #endif // FEATURE_HW_INTRINSICS
8858
+
8859
+ // TODO-CQ: Returning this as an intrinsic blocks inlining and is undesirable
8860
+ // return impMathIntrinsic(method, sig, callType, intrinsicName, tailCall);
8861
+
8862
+ return nullptr;
8863
+ }
8864
+
8731
8865
GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
8732
8866
CORINFO_SIG_INFO* sig,
8733
8867
var_types callType,
@@ -10337,7 +10471,20 @@ NamedIntrinsic Compiler::lookupPrimitiveFloatNamedIntrinsic(CORINFO_METHOD_HANDL
10337
10471
10338
10472
case 'R':
10339
10473
{
10340
- if (strcmp(methodName, "Round") == 0)
10474
+ if (strncmp(methodName, "Reciprocal", 10) == 0)
10475
+ {
10476
+ methodName += 10;
10477
+
10478
+ if (strcmp(methodName, "Estimate") == 0)
10479
+ {
10480
+ result = NI_System_Math_ReciprocalEstimate;
10481
+ }
10482
+ else if (strcmp(methodName, "SqrtEstimate") == 0)
10483
+ {
10484
+ result = NI_System_Math_ReciprocalSqrtEstimate;
10485
+ }
10486
+ }
10487
+ else if (strcmp(methodName, "Round") == 0)
10341
10488
{
10342
10489
result = NI_System_Math_Round;
10343
10490
}
0 commit comments