Repro:
internalrefstructFoo{privatereadonlySpan<int>_span;internalFoo(Span<int>span)=>_span=span;internalvoidSet(intindex){if((uint)index<(uint)_span.Length){_span[index]=42;}}}Codegen (x64):
subrsp,38xoreax,eaxmov[rsp+28],raxmov[rsp+30],raxmovrax,[rcx+8]testrax,raxje short M00_L02leardx,[rax+10]moveax,[rax+8]M00_L00:learcx,[rsp+28]mov[rcx],rdxmov[rcx+8],eaxcmp dword ptr [rsp+30],3jbe short M00_L01leardx,[rsp+28]cmp dword ptr [rdx+8],3 ; redundant check for bound-check -- should be elidedjbe short M00_L03movrax,[rdx]mov dword ptr [rax+0C],2AM00_L01:addrsp,38retM00_L02:xoredx,edxxoreax,eaxjmp short M00_L00M00_L03:call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 88
Workaround: fetch the span to a local.
+Span<int> span = _span;-if ((uint)index < (uint)_span.Length)+if ((uint)index < (uint)span.Length)
{
- _span[index] = 42;+ span[index] = 42;
}When the index is not constant (here 3), the same issue occurs.
Complete listing of code (C#)
usingBenchmarkDotNet.Attributes;BenchmarkDotNet.Running.BenchmarkRunner.Run<Bench>();[DisassemblyDiagnoser][ShortRunJob]publicclassBench{privatereadonlyint[]_array=newint[100];[Benchmark]publicvoidNoLocalSpan(){Foofoo=new(_array);foo.Set(3);}[Benchmark]publicvoidLocalSpan(){Foo1foo=new(_array);foo.Set(3);}}internalrefstructFoo{privatereadonlySpan<int>_span;internalFoo(Span<int>span)=>_span=span;internalvoidSet(intindex){if((uint)index<(uint)_span.Length){_span[index]=42;}}}internalrefstructFoo1{privatereadonlySpan<int>_span;internalFoo1(Span<int>span)=>_span=span;internalvoidSet(intindex){Span<int>span=_span;if((uint)index<(uint)span.Length){span[index]=42;}}}Complete listing of generated code
; Bench.NoLocalSpan()subrsp,38xoreax,eaxmov[rsp+28],raxmov[rsp+30],raxmovrax,[rcx+8]testrax,raxje short M00_L02leardx,[rax+10]moveax,[rax+8]M00_L00:learcx,[rsp+28]mov[rcx],rdxmov[rcx+8],eaxcmp dword ptr [rsp+30],3jbe short M00_L01leardx,[rsp+28]cmp dword ptr [rdx+8],3jbe short M00_L03movrax,[rdx]mov dword ptr [rax+0C],2AM00_L01:addrsp,38retM00_L02:xoredx,edxxoreax,eaxjmp short M00_L00M00_L03:call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 88; Bench.LocalSpan()subrsp,18xoreax,eaxmov[rsp+8],raxmov[rsp+10],raxmovrax,[rcx+8]testrax,raxje short M00_L02leardx,[rax+10]moveax,[rax+8]M00_L00:learcx,[rsp+8]mov[rcx],rdxmov[rcx+8],eaxleardx,[rsp+8]movrax,[rdx]movedx,[rdx+8]cmpedx,3jbe short M00_L01mov dword ptr [rax+0C],2AM00_L01:addrsp,18retM00_L02:xoredx,edxxoreax,eaxjmp short M00_L00; Total bytes of code 77
Cf. #67448 (comment)
category:cq
theme:bounds-checks
skill-level:intermediate
cost:small
impact:small
Repro:
Codegen (x64):
Workaround: fetch the span to a local.
When the index is not constant (here 3), the same issue occurs.
Complete listing of code (C#)
Complete listing of generated code
Cf. #67448 (comment)
category:cq
theme:bounds-checks
skill-level:intermediate
cost:small
impact:small