JIT: (bug) Half reciprocal estimate intrinsics bypass ReadyToRun non-deterministic intrinsic policy
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
`Half.ReciprocalEstimate` and `Half.ReciprocalSqrtEstimate` can be expanded into hardware estimate instructions in ReadyToRun images, bypassing the policy that blocks non-deterministic estimate intrinsics from R2R code.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
public static class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static Half Test(Half x) => Half.ReciprocalEstimate(x);
[MethodImpl(MethodImplOptions.NoInlining)]
public static Half TestRSqrt(Half x) => Half.ReciprocalSqrtEstimate(x);
[MethodImpl(MethodImplOptions.NoInlining)]
public static float TestF(float x) => MathF.ReciprocalEstimate(x);
[MethodImpl(MethodImplOptions.NoInlining)]
public static float TestFRSqrt(float x) => MathF.ReciprocalSqrtEstimate(x);
public static void Main()
{
Console.WriteLine(Test((Half)3.0f));
Console.WriteLine(TestRSqrt((Half)3.0f));
Console.WriteLine(TestF(3.0f));
Console.WriteLine(TestFRSqrt(3.0f));
}
}
```
Reproduces when crossgen2 compiles the assembly as ReadyToRun with an estimate-capable target ISA such as `avx10v1`.
### Expected
The estimate APIs stay as calls in the R2R image, matching the `MathF` estimate intrinsics and preserving deterministic ReadyToRun behavior across hardware.
### Actual
The `Half` estimate overloads bake hardware estimate instructions into the R2R body, while the equivalent `MathF` APIs remain calls:
```text
; Program:Test(System.Half):System.Half
vrcpsh xmm0, xmm1, xmm0
; Program:TestRSqrt(System.Half):System.Half
vrsqrtsh xmm0, xmm1, xmm0
; Program:TestF(float):float
call [System.MathF:ReciprocalEstimate(float):float]
; Program:TestFRSqrt(float):float
call [System.MathF:ReciprocalSqrtEstimate(float):float]
```
### Notes
`Half.Sqrt` shares the importer case but is IEEE-exact; only the two estimate intrinsics need the guard.
The `Half` intrinsic path uses `compOpportunisticallyDependsOn` and never calls `BlockNonDeterministicIntrinsics`, unlike `Compiler::impEstimateIntrinsic` for `MathF`.
A likely fix is to split the `Half` estimate cases and apply the same R2R blocking and exact-ISA dependency policy as the existing estimate intrinsic path.
Contributor guide
Research direction
Reproduce the issue with crossgen2 and an estimate-capable target such as avx10v1, then compare the Half importer cases with Compiler::impEstimateIntrinsic for MathF. Trace the compOpportunisticallyDependsOn path and the non-deterministic intrinsic policy. Done means Half reciprocal estimate APIs remain calls in ReadyToRun images while Half.Sqrt remains IEEE-exact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100