JIT: (bug) Span<T>.get_Item intrinsic evaluates the index before the receiver
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
The optimized importer expansion for `Span.get_Item` and `ReadOnlySpan.get_Item` can spill/evaluate the index before the receiver, violating IL evaluation order and producing wrong results when the receiver mutates state read by the index.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
public class Program
{
static int s_idx;
[MethodImpl(MethodImplOptions.NoInlining)]
static ref Span GetSpan(ref Span s) { s_idx = 1; return ref s; }
[MethodImpl(MethodImplOptions.NoInlining)]
static ref ReadOnlySpan GetROSpan(ref ReadOnlySpan s) { s_idx = 1; return ref s; }
[MethodImpl(MethodImplOptions.NoInlining)]
static int[] GetArr(int[] a) { s_idx = 1; return a; }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int TestSpan(ref Span s) => GetSpan(ref s)[s_idx];
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int TestROSpan(ref ReadOnlySpan s) => GetROSpan(ref s)[s_idx];
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int TestArr(int[] a) => GetArr(a)[s_idx];
public static void Main()
{
int[] data = { 10, 20 };
Span span = data;
ReadOnlySpan rospan = data;
s_idx = 0;
Console.WriteLine("span: " + TestSpan(ref span));
s_idx = 0;
Console.WriteLine("rosp: " + TestROSpan(ref rospan));
s_idx = 0;
Console.WriteLine("arr : " + TestArr(data));
}
}
```
### Expected
```text
span: 20
rosp: 20
arr : 20
```
### Actual
```text
span: 10
rosp: 10
arr : 20
```
### Notes
The intrinsic path pops receiver and index, then clones/spills the index first; since the receiver has already been popped, that spill is appended before the side-effecting receiver evaluation.
The array indexer path preserves the required receiver-before-index order, so it returns `20`.
Contributor guide
Assessment
This issue has not been assessed yet.