The JIT expansion of WithElement emits the index range check ahead of the value argument, so when the index is out of range the value expression's side effects are dropped entirely — violating C# left-to-right argument evaluation.
Minimal Repro
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
public static class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static float Value() { Console.WriteLine("Value() ran"); return 42.0f; }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static Vector128<float> Test(Vector128<float> v, int idx) => v.WithElement(idx, Value());
static void Main()
{
try { Test(Vector128<float>.Zero, 99); }
catch (Exception e) { Console.WriteLine(e.GetType().Name); }
}
}
Expected
Value() ran
ArgumentOutOfRangeException
Actual
ArgumentOutOfRangeException
Notes
Correct output with DOTNET_EnableHWIntrinsic=0 (managed fallback). Also repros on released .NET 10.0.12.
Not float- or width-specific: Vector256<float> and Vector128<int> behave the same, and a null receiver in the value argument (v.WithElement(idx, h.F)) yields ArgumentOutOfRangeException instead of NullReferenceException.
Compiler::gtNewSimdWithElementNode (gentree.cpp) calls addRangeCheckForHWIntrinsic on op2, rewriting the index into COMMA(BOUNDS_CHECK, index), without first spilling op3 to a temp; the check is above the per-target #if, so all targets should be affected.
The JIT expansion of
WithElementemits the index range check ahead of thevalueargument, so when the index is out of range the value expression's side effects are dropped entirely — violating C# left-to-right argument evaluation.Minimal Repro
Expected
Actual
Notes
Correct output with
DOTNET_EnableHWIntrinsic=0(managed fallback). Also repros on released .NET 10.0.12.Not float- or width-specific:
Vector256<float>andVector128<int>behave the same, and anullreceiver in the value argument (v.WithElement(idx, h.F)) yieldsArgumentOutOfRangeExceptioninstead ofNullReferenceException.Compiler::gtNewSimdWithElementNode(gentree.cpp) callsaddRangeCheckForHWIntrinsiconop2, rewriting the index intoCOMMA(BOUNDS_CHECK, index), without first spillingop3to a temp; the check is above the per-target#if, so all targets should be affected.