dotnet / dotnet/runtime

JIT: (bug) Half reciprocal estimate intrinsics bypass ReadyToRun non-deterministic intrinsic policy

Open
#133,962 1 comment 0 reactions 0 assignees View on GitHub
area-CodeGen-coreclr
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.