dotnet / dotnet/runtime

JIT: (bug) Assertion failed '!node->IsUserCall()' during 'Rationalize IR' with an identity Vector128.Shuffle

Open
#133,787 0 comments 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

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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.