Skip to content

Codegen: Bound check not elided in ref struct Span field index access #72004

Description

@gfoidl

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

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

Relationships

None yet

Development

No branches or pull requests

Issue actions