JIT: assertion 'FitsIn<int32_t>(value)' on constant-folded BitOperations.RotateRight(0xFFFFFFFFu, k) with multiple uses
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
> [!NOTE]
> This issue was authored by GitHub Copilot CLI on @AndyAyersMS's machine,
> based on a bug surfaced by an experimental in-development fuzzer.
> The C# repro is verified.
### Repro
```csharp
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
public static class Program
{
private static uint Sink;
[MethodImpl(MethodImplOptions.NoInlining)]
public static uint Fn()
{
uint v2 = BitOperations.RotateRight(0xFFFFFFFFu, 1);
Sink = v2;
return v2;
}
public static int Main()
{
Console.WriteLine($"{Fn():X8}");
return 0;
}
}
```
### Observed
| Configuration | Result |
|---|---|
| Local `dotnet/runtime` HEAD Checked, `DOTNET_TieredCompilation=0` (FullOpts) | **JIT assertion** `'FitsIn(value)'` in `src/coreclr/jit/compiler.hpp:2100` during `'Morph - Global'` |
| Local HEAD `DOTNET_JITMinOpts=1` | `FFFFFFFF` ✅ |
| Shipping `dotnet 11.0.0-preview.3.26207.106` (any config, Release) | `FFFFFFFF` ✅ |
Same regression window as #129076 (between preview-3 and HEAD); haven't bisected this one yet.
Asserts on `osx-arm64 Checked` at HEAD `a8b2c92ce21`. Whether Release builds produce wrong code is unverified — I don't have a Release runtime build handy. The assertion is at `GenTreeIntCon::SetIconValue`-style code path, which is normally a "load-bearing" invariant rather than a debug-only sanity check.
### Required ingredients
All necessary, all sufficient when combined:
1. `BitOperations.RotateRight(0xFFFFFFFFu, k)` for any `k >= 1` (rotation count > 0)
2. Result stored into a local
3. Two uses of that local — one into a side-effecting target (here a static field), one as the return value
Removing any of these stops the crash. So:
- Direct `return BitOperations.RotateRight(0xFFFFFFFFu, 1);` (single use, no local) — no crash, returns `FFFFFFFF`
- Different constant input, e.g. `BitOperations.RotateRight(0xFFFFFFFEu, 1)` — no crash, returns `7FFFFFFF`
- ulong overload, `BitOperations.RotateRight(0xFFFFFFFFFFFFFFFFul, 1)` — no crash, returns `FFFFFFFFFFFFFFFF`
### Hypothesis
The JIT recognizes `BitOperations.RotateRight(uint, int)` as an intrinsic and constant-folds it when both inputs are constants. The folded value is `0xFFFFFFFF`. With two uses, CSE produces a shared constant node typed `uint` with value `0xFFFFFFFF`. Something during `Morph - Global` then tries to write that constant value through a `GenTreeIntCon::SetIconValue`-style path with `type == TYP_INT`, which asserts `FitsIn(value)` and trips because `0xFFFFFFFF = 4294967295 > int.MaxValue = 2147483647`.
The ulong-not-affected datapoint is consistent: that code path is gated on `genTypeSize(type) <= genTypeSize(TYP_INT)`.
### How it was found
ReifyCs (semantic-reification fuzzer, same one that found #129076) with a new `--profile intrinsic` populator that emits `BitOperations.RotateLeft/Right` and `Math.Abs/Max/Min` calls. Surfaced on the first 50-trial smoke campaign of that profile. Hand-reduced from a ~60-line generated function to the 6-line repro above.
Contributor guide
Assessment
This issue has not been assessed yet.