JIT: (bug) Assertion failed '!node->IsUserCall()' during 'Rationalize IR' with an identity Vector128.Shuffle
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
When `Rationalizer::RewriteHWIntrinsicAsUserCall` replaces a deferred intrinsic with a *replacement node*, the pre-order visit is not re-run on that replacement. A deferred (`GTF_HW_USER_CALL`) intrinsic nested inside it is then never rewritten into a user call, and the post-order assert fires.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
public static class P
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static Vector128 Test(Vector128 v, byte n)
{
Vector128 idx = Vector128.Create((byte)0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
return Vector128.Shuffle(Sse2.ShiftRightLogical128BitLane(v, n), idx);
}
public static void Main()
{
Console.WriteLine(Test(Vector128.Create((byte)1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), 1));
}
}
```
### Expected
```
<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0>
```
### Actual
```
Assert failure(PID 29500 [0x0000733c], Thread: 38784 [0x9780]): Assertion failed '!node->IsUserCall()' in 'P:Test(System.Runtime.Intrinsics.Vector128`1[byte],byte):System.Runtime.Intrinsics.Vector128`1[byte]' during 'Rationalize IR' (IL size 43; hash 0x86daef64; FullOpts)
File: src\coreclr\jit\rationalize.cpp:595
```
Exit code `0xC0000409` (fail-fast).
### Notes
- `Sse2.ShiftRightLogical128BitLane(v, n)` has a non-constant immediate, so it is imported as a deferred user-call intrinsic with a managed fallback handle.
- The outer `Vector128.Shuffle` is also deferred at import, but optimization turns the indices into the constant identity permutation before rationalization, so `gtNewSimdShuffleNode` returns `op1` unchanged.
- On the `result != nullptr` path in `RewriteHWIntrinsicAsUserCall`, `parents.Push(result)` happens without re-running the pre-order visit for `result`, leaving the inner deferred node unconverted.
- Checked-only today: a Release runtime prints the expected output, no observable bad codegen.
Contributor guide
Research direction
Start in src/coreclr/jit/rationalize.cpp at Rationalizer::RewriteHWIntrinsicAsUserCall, then trace the result != nullptr path and gtNewSimdShuffleNode behavior described in the issue. Use the provided C# Vector128.Shuffle repro to verify the checked runtime no longer asserts during Rationalize IR and produces the expected vector output.
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
- Clearly specified
- Newbie friendliness
- 58/100