Skip to content

Span bounds check elision doesn't work properly when method calls occur between bounds check and access #49113

Closed
@GrabYourPitchforks

Description

@GrabYourPitchforks

If the caller performs a bounds check on a span, the runtime will not elide future bounds checks satisfied by that condition if a method call occurs between the original check and the subsequent access. The following repro code demonstrates this issue.

using System;

public class MyClass {
    public int SomeField;
    public int SomeProperty => 42;
    public int SomeMethod() => 42;
    
    private void Foo(Span<int> destination)
    {
        if (!destination.IsEmpty)
        {
            destination[0] = SomeProperty;
        }    
    }
    
    private void Bar(Span<int> destination)
    {
        if (!destination.IsEmpty)
        {
            destination[0] = SomeField;
        }    
    }
    
    private void Baz(Span<int> destination)
    {
        if (!destination.IsEmpty)
        {
            destination[0] = SomeMethod();
        }    
    }
}

x64 codegen, courtesy SharpLab:

MyClass.Foo(System.Span`1<Int32>)
    L0000: sub rsp, 0x28
    L0004: mov rax, [rdx]
    L0007: mov edx, [rdx+8]
    L000a: test edx, edx  ; call to destination.IsEmpty
    L000c: jbe short L0019
    L000e: cmp edx, 0  ; bounds check should've been elided
    L0011: jbe short L001e
    L0013: mov dword ptr [rax], 0x2a
    L0019: add rsp, 0x28
    L001d: ret
    L001e: call 0x00007ff9f75fbab0
    L0023: int3

MyClass.Bar(System.Span`1<Int32>)
    L0000: mov rax, [rdx]
    L0003: mov edx, [rdx+8]
    L0006: test edx, edx  ; call to destination.IsEmpty
    L0008: jbe short L000f
    L000a: mov edx, [rcx+8]
    L000d: mov [rax], edx  ; no bounds check before writing field value to destination
    L000f: ret

MyClass.Baz(System.Span`1<Int32>)
    L0000: sub rsp, 0x28
    L0004: mov rax, [rdx]
    L0007: mov edx, [rdx+8]
    L000a: test edx, edx  ; call to destination.IsEmpty
    L000c: jbe short L0019
    L000e: cmp edx, 0  ; bounds check should've been elided
    L0011: jbe short L001e
    L0013: mov dword ptr [rax], 0x2a
    L0019: add rsp, 0x28
    L001d: ret
    L001e: call 0x00007ff9f75fbab0
    L0023: int3

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    Status

    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions