Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bounds checks on array/span not eliminated after length check #10596

Closed
stephentoub opened this issue Jun 28, 2018 · 8 comments
Closed

Bounds checks on array/span not eliminated after length check #10596

stephentoub opened this issue Jun 28, 2018 · 8 comments
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI enhancement Product code improvement that does NOT require public API changes/additions JitUntriaged CLR JIT issues needing additional triage optimization
Milestone

Comments

@stephentoub
Copy link
Member

I've got code similar to the following repro:

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

public class C
{
    public static void Main() => new C().TryFormat(new char[4], out _);

    public bool TryFormat(Span<char> dst, out int charsWritten)
    {
        if (dst.Length >= 4)
        {
            dst[0] = 't';
            dst[1] = 'r';
            dst[2] = 'u';
            dst[3] = 'e';
            charsWritten = 4;
            return true;
        }
        charsWritten = 0;
        return false;
    }
}

I was hoping/expecting the bounds checks on each of those four writes to dst to be eliminated, but they’re not:

G_M404_IG02:
       488B02               mov      rax, bword ptr [rdx]
       8B5208               mov      edx, dword ptr [rdx+8]
       83FA04               cmp      edx, 4
       7C3C                 jl       SHORT G_M404_IG04
       83FA00               cmp      edx, 0
       7641                 jbe      SHORT G_M404_IG06
       66C7007400           mov      word  ptr [rax], 116
       83FA01               cmp      edx, 1
       7637                 jbe      SHORT G_M404_IG06
       66C740027200         mov      word  ptr [rax+2], 114
       83FA02               cmp      edx, 2
       762C                 jbe      SHORT G_M404_IG06
       66C740047500         mov      word  ptr [rax+4], 117
       83FA03               cmp      edx, 3
       7621                 jbe      SHORT G_M404_IG06
       66C740066500         mov      word  ptr [rax+6], 101
       41C70004000000       mov      dword ptr [r8], 4
       B801000000           mov      eax, 1

To work around that, I can use Unsafe.Add and MemoryMarshal.GetReference, e.g.

public bool TryFormat(Span<char> dst, out int charsWritten)
{
    if (dst.Length >= 4)
    {
        ref char c = ref MemoryMarshal.GetReference(dst);
        c = 't';
        Unsafe.Add(ref c, 1) = 'r';
        Unsafe.Add(ref c, 2) = 'u';
        Unsafe.Add(ref c, 3) = 'e';
        charsWritten = 4;
        return true;
    }
    charsWritten = 0;
    return false;
}

in which case I get the better:

G_M408_IG02:
       837A0804             cmp      dword ptr [rdx+8], 4
       7C27                 jl       SHORT G_M408_IG04
       488B02               mov      rax, bword ptr [rdx]
       66C7007400           mov      word  ptr [rax], 116
       66C740027200         mov      word  ptr [rax+2], 114
       66C740047500         mov      word  ptr [rax+4], 117
       66C740066500         mov      word  ptr [rax+6], 101
       41C70004000000       mov      dword ptr [r8], 4
       B801000000           mov      eax, 1

but it’d be nice not to have to use Unsafe for cases like this.

cc: @AndyAyersMS
Related: https://github.com/dotnet/coreclr/issues/12639

category:cq
theme:range-check
skill-level:intermediate
cost:medium

@mikedn
Copy link
Contributor

mikedn commented Jun 28, 2018

but it’d be nice not to have to use Unsafe for cases like this.

This works too:

        if (3 < (uint)dst.Length)
        {
            dst[0] = 't';
            dst[1] = 'r';
            dst[2] = 'u';
            dst[3] = 'e';
            charsWritten = 4;
            return true;
        }
        charsWritten = 0;
        return false;

Hmm, maybe I can make it work without the cast to uint. That's normally needed to ensure that the index is positive but in the case of constants this is obvious.

@stephentoub
Copy link
Member Author

This works too

Interesting, thanks. Yeah, I see (uint)dst.Length > 3 works, but (uint)dst.Length >= 4 does not. That seems... arbitrary :)

@mikedn
Copy link
Contributor

mikedn commented Jun 28, 2018

but (uint)dst.Length >= 4 does not

Right, that's another thing that can probably be improved in the case of constants. For variables something like dst.Length >= x is not of much use, unless perhaps you then have dst[x - 1]. That's something that the RangeCheck phase should be able to handle but it's so focused on dealing with loops that it pretty much misses anything else.

@tgiphil
Copy link
Contributor

tgiphil commented Jun 29, 2018

Off-topic question: How was the disassembly of the method generated? Thanks in advance.

@stephentoub
Copy link
Member Author

How was the disassembly of the method generated?

With a checked build of coreclr (e.g. build.cmd -checked -skiptests), and then when running the repro with it, setting the COMPlus_JitDisasm environment variable to the name of the method, e.g.

set COMPlus_JitDisasm=TryFormat

@erozenfeld
Copy link
Member

@dotnet/jit-contrib

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@BruceForstall BruceForstall added the JitUntriaged CLR JIT issues needing additional triage label Oct 28, 2020
@gfoidl
Copy link
Member

gfoidl commented Mar 31, 2022

@EgorBo this issue is resolved and can be closed?

@EgorBo
Copy link
Member

EgorBo commented Mar 31, 2022

@gfoidl yes, I've just checked, thanks for pointing that out!

@EgorBo EgorBo closed this as completed Mar 31, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 4, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI enhancement Product code improvement that does NOT require public API changes/additions JitUntriaged CLR JIT issues needing additional triage optimization
Projects
None yet
Development

No branches or pull requests

8 participants