Skip to content

JIT doesn't eliminate redundant branch in guarded span.Slice #65327

Closed
@stephentoub

Description

@stephentoub

SharpLab

using System;
public class C
{
    public ReadOnlySpan<char> M1(ReadOnlySpan<char> span, int i)
    {
        if ((uint)i < (uint)span.Length)
        {
            return span.Slice(i);
        }
        return default;
    }
    
    public ReadOnlySpan<char> M2(ReadOnlySpan<char> span, int i)
    {
        if ((uint)i <= (uint)span.Length)
        {
            return span.Slice(i);
        }
        return default;
    }
}
C.M1(System.ReadOnlySpan`1<Char>, Int32)
    L0000: sub rsp, 0x28
    L0004: mov rax, rdx
    L0007: mov rdx, [r8]
    L000a: mov ecx, [r8+8]
    L000e: cmp r9d, ecx
    L0011: jae short L002d
    L0013: cmp r9d, ecx
    L0016: ja short L003c
    L0018: sub ecx, r9d
    L001b: mov r8d, r9d
    L001e: lea rdx, [rdx+r8*2]
    L0022: mov [rax], rdx
    L0025: mov [rax+8], ecx
    L0028: add rsp, 0x28
    L002c: ret
    L002d: xor edx, edx
    L002f: mov [rax], rdx
    L0032: xor edx, edx
    L0034: mov [rax+8], edx
    L0037: add rsp, 0x28
    L003b: ret
    L003c: call 0x00007ffb1bc85778
    L0041: int3

C.M2(System.ReadOnlySpan`1<Char>, Int32)
    L0000: mov rax, rdx
    L0003: mov rdx, [r8]
    L0006: mov ecx, [r8+8]
    L000a: cmp r9d, ecx
    L000d: ja short L0020
    L000f: sub ecx, r9d
    L0012: mov r8d, r9d
    L0015: lea rdx, [rdx+r8*2]
    L0019: mov [rax], rdx
    L001c: mov [rax+8], ecx
    L001f: ret
    L0020: xor edx, edx
    L0022: mov [rax], rdx
    L0025: xor edx, edx
    L0027: mov [rax+8], edx
    L002a: ret

ReadOnlySpan<T>.Slice contains the following guard clause:

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, (nint)(uint)start /* force zero-extension */), _length - start);
}

In M1, we've guaranteed we're only calling span.Slice(i) if i is less than span.Length, but the JIT doesn't see that it can then eliminate the branch in Slice for when i is greater than span.Length.

In M2, the branch does get eliminated, presumably since it exactly matches the condition used by Slice.

cc: @AndyAyersMS

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions