I've chased the exact cause for this issue for a long long time, and I finally managed to narrow it down to a reproducible issue.
Environment
I'm using .NET 5.0 preview 3:
dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 5.0.100-preview.3.20216.6
Commit: 9f62a32109
Runtime Environment:
OS Name: clear-linux-os
OS Version: 32910
OS Platform: Linux
RID: linux-x64
Base Path: /home/dmg/dotnet/sdk/5.0.100-preview.3.20216.6/
Host (useful for support):
Version: 5.0.0-preview.3.20214.6
Commit: b037784658
.NET SDKs installed:
3.1.101 [/home/dmg/dotnet/sdk]
3.1.102 [/home/dmg/dotnet/sdk]
3.1.201 [/home/dmg/dotnet/sdk]
5.0.100-preview.3.20216.6 [/home/dmg/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 3.1.1 [/home/dmg/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.2 [/home/dmg/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.3 [/home/dmg/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.0-preview.3.20215.14 [/home/dmg/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.1 [/home/dmg/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.2 [/home/dmg/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.3 [/home/dmg/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.0-preview.3.20214.6 [/home/dmg/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
Buggy JIT behavior
The issue can be clearly seen when comparing the generated ASM between this buggy version:
Where in this order of variable declarations:
var tmpLeft = _tempStart;
var tmpRight = _tempEnd - N;
var writeLeft = left;
var writeRight = right - N - 1;
var pBase = Int32PermTables.IntPermTablePtr;
Where the writeRight variable is declared before the pBase pointer is initialized, leads to the JIT incorrectly deciding to NOT promote writeRight into a register, later within the function:
M08_L00:
mov rcx,rsi
sub rcx,rax
mov r8,rcx
sar r8,3F
and r8,3
add rcx,r8
sar rcx,2
mov r8,[rbp-38] ; This is writeRight!
mov r9,r8
sub r9,rdx
mov r10,r9
sar r10,3F
and r10,3
add r9,r10
sar r9,2
cmp rcx,r9
jg short M08_L02
lea rcx,[rsi+20]
jmp short M08_L03
Workaround / Bug
By changing the order of declaration, as I did in my workaround version:
// This time pBase is initialized BEFORE the other variables
var pBase = Int32PermTables.IntPermTablePtr;
var tmpLeft = _tempStart;
var tmpRight = _tempEnd - N;
var writeLeft = left;
var writeRight = right - N - 1;
The generated assembly now correctly promotes the writeRight variable into a full fledged register without performing needless stack reads and write within my main loop:
M08_L00:
mov r8,rax
sub r8,r15
mov r9,r8
sar r9,3F
and r9,3
add r8,r9
sar r8,2
mov r9,rdx
sub r9,rcx
mov r10,r9
sar r10,3F
and r10,3
add r9,r10
sar r9,2
cmp r8,r9
jg short M08_L02
lea r8,[rax+20]
jmp short M08_L03
The full disassembly can be viewed here, in case someone is interested:
https://gist.github.com/damageboy/c09fc710d2b9010fe176645fa526a170
category:cq
theme:register-allocator
skill-level:expert
cost:medium
I've chased the exact cause for this issue for a long long time, and I finally managed to narrow it down to a reproducible issue.
Environment
I'm using .NET 5.0 preview 3:
Buggy JIT behavior
The issue can be clearly seen when comparing the generated ASM between this buggy version:
Where in this order of variable declarations:
Where the
writeRightvariable is declared before thepBasepointer is initialized, leads to the JIT incorrectly deciding to NOT promotewriteRightinto a register, later within the function:Workaround / Bug
By changing the order of declaration, as I did in my workaround version:
The generated assembly now correctly promotes the
writeRightvariable into a full fledged register without performing needless stack reads and write within my main loop:The full disassembly can be viewed here, in case someone is interested:
https://gist.github.com/damageboy/c09fc710d2b9010fe176645fa526a170
category:cq
theme:register-allocator
skill-level:expert
cost:medium