Repository navigation
RyuJIT's loop cloning optimization has questionable CQ and a bug #4922
Description
Activity
I took a quick look at the code and my initial impression is that this happens because the optimizer works on do-while loops and as such it doesn't "see" the
i < lengthcheck at the start of the function.But then the CQ is a minor issue, the real problem is
[MethodImpl(MethodImplOptions.NoInlining)] static void Copy(int[] src, int[] dst, int length) { int i = 50000; do { dst[i] = src[i]; i++; } while (i < length); } static void Main() { Copy(new int[2], new int[2], 1); }
which promptly throws an AccessViolationException
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Program.Copy(Int32[] src, Int32[] dst, Int32 length) at Program.Main()- changed the title
[-]RyuJIT's loop cloning optimization has questionable CQ[/-][+]RyuJIT's loop cloning optimization has questionable CQ and a bug[/+]on Jan 10, 2016 I cannot repro that in .NET 4.6.1, I get an
IndexOutOfRangeExceptionas expected. Code generated is:00007ffc`a8fc0570 56 push rsi 00007ffc`a8fc0571 4883ec20 sub rsp,20h 00007ffc`a8fc0575 b850c30000 mov eax,0C350h 00007ffc`a8fc057a 448b4908 mov r9d,dword ptr [rcx+8] 00007ffc`a8fc057e 448b5208 mov r10d,dword ptr [rdx+8] 00007ffc`a8fc0582 413bc1 cmp eax,r9d 00007ffc`a8fc0585 7322 jae 00007ffc`a8fc05a9 00007ffc`a8fc0587 4c63d8 movsxd r11,eax 00007ffc`a8fc058a 468b5c9910 mov r11d,dword ptr [rcx+r11*4+10h] 00007ffc`a8fc058f 413bc2 cmp eax,r10d 00007ffc`a8fc0592 7315 jae 00007ffc`a8fc05a9 00007ffc`a8fc0594 4863f0 movsxd rsi,eax 00007ffc`a8fc0597 44895cb210 mov dword ptr [rdx+rsi*4+10h],r11d 00007ffc`a8fc059c ffc0 inc eax 00007ffc`a8fc059e 413bc0 cmp eax,r8d 00007ffc`a8fc05a1 7cdf jl 00007ffc`a8fc0582 00007ffc`a8fc05a3 4883c420 add rsp,20h 00007ffc`a8fc05a7 5e pop rsi 00007ffc`a8fc05a8 c3 ret 00007ffc`a8fc05a9 e85a14a85f call clr!JIT_RngChkFail (00007ffd`08a41a08) 00007ffc`a8fc05ae cc int 3It does not look like RyuJIT in .NET 4.6.1 does loop cloning for this case? Seems weird since history of the Git repo seems to indicate that loop cloning received no changes after .NET 4.6.1...
EDIT: The
for (int i = 0; i < length; i++)version is loop cloned correctly in .NET 4.6.1.It does not look like RyuJIT in .NET 4.6.1 does loop cloning for this case? Seems weird since history of the Git repo seems to indicate that loop cloning received no changes after .NET 4.6.1...
Yes, for some reason .NET 4.6.1 doesn't do loop cloning for such do-while loops. It doesn't do it even if you change the do-while version to be equivalent to the for version:
int i = 0; if (i < length) { do { dst[i] = src[i]; i++; } while (i < length); }
The C# compiler generates slightly different IL for such a loop and .NET 4.6.1, unlike CoreCLR, doesn't like it. It's possible that a fix was already made in .NET 4.6.1 but it is yet to make it to this repository.
@mikedn but it is a bug that the code throws an AV, right? Is this CoreCLR or .NET 4.6.1 also?
@CppStars Yes, it's a bug and as far as I can tell it's present only in CoreCLR. The AV is just for the "show", I intentionally picked a large initial index to trigger an observable AV. For smaller values you'll end up with silent memory corruption.
As far as I can tell this bug is triggered only by a do-while loop with a constant initial index. Those are rare circumstances I'd say.
@mikedn dotnet/coreclr#2627 should have resolved the correctness issue. dotnet/coreclr#2634 opened to track the CQ issue.
- ghost locked as resolved and limited conversation to collaborators
on Jan 3, 2021
The following method
generates
@JanielS speculated in #4921 that the null checks may be there so the NullReferenceException will appears as if it was thrown from inside the loop instead of being thrown form hoisted code. But if you pass a null array to this method the debugger will point to the
i < lengthloop condition when the exception is thrown, not todst[i] = src[i].