Skip to content

JIT: (bug) Span<T>.get_Item intrinsic evaluates the index before the receiver #133963

Description

@EgorBo

The optimized importer expansion for Span<T>.get_Item and ReadOnlySpan<T>.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

using System;
using System.Runtime.CompilerServices;

public class Program
{
    static int s_idx;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static ref Span<int> GetSpan(ref Span<int> s) { s_idx = 1; return ref s; }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static ref ReadOnlySpan<int> GetROSpan(ref ReadOnlySpan<int> 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<int> s) => GetSpan(ref s)[s_idx];

    [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
    static int TestROSpan(ref ReadOnlySpan<int> 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<int> span = data;
        ReadOnlySpan<int> 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

span: 20
rosp: 20
arr : 20

Actual

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions