Description
Description
When accessing span items by Index from end, some patterns do not eliminate the bounds check.
Reproduction Steps
Access a span by both its' first and last item. Both accesses incur an index bounds check, despite the s[0]
ensuring the span is non-empty. Accessing s[^1]
first eliminates the second bounds check.
IsEmpty
and an explicit Length != 0
checks seem to work properly, e.g. s.IsEmpty ? -1 : s[0]
.
Expected behavior
public static int Bounds2(ReadOnlySpan<byte> s)
{
byte sz = s[^1];
byte s0 = s[0];
return s0 + sz;
}
; Method ConsoleApp7.TestClass:Bounds2(System.ReadOnlySpan`1[ubyte]):int (FullOpts)
G_M000_IG01: ;; offset=0x0000
sub rsp, 40
G_M000_IG02: ;; offset=0x0004
mov rax, bword ptr [rcx]
mov ecx, dword ptr [rcx+0x08]
lea edx, [rcx-0x01]
cmp edx, ecx
jae SHORT G_M000_IG04
mov ecx, edx
movzx rcx, byte ptr [rax+rcx]
movzx rax, byte ptr [rax]
add eax, ecx
G_M000_IG03: ;; offset=0x001C
add rsp, 40
ret
G_M000_IG04: ;; offset=0x0021
call CORINFO_HELP_RNGCHKFAIL
int3
; Total bytes of code: 39
Actual behavior
Both accesses cause a range check.
public static int Bounds1(ReadOnlySpan<byte> s)
{
byte s0 = s[0];
byte sz = s[^1];
return s0 + sz;
}
; Method ConsoleApp7.TestClass:Bounds1(System.ReadOnlySpan`1[ubyte]):int (FullOpts)
G_M000_IG01: ;; offset=0x0000
sub rsp, 40
G_M000_IG02: ;; offset=0x0004
mov rax, bword ptr [rcx]
mov ecx, dword ptr [rcx+0x08]
test ecx, ecx
je SHORT G_M000_IG04
movzx rdx, byte ptr [rax]
lea r8d, [rcx-0x01]
cmp r8d, ecx
jae SHORT G_M000_IG04
mov ecx, r8d
movzx rax, byte ptr [rax+rcx]
add eax, edx
G_M000_IG03: ;; offset=0x0023
add rsp, 40
ret
G_M000_IG04: ;; offset=0x0028
call CORINFO_HELP_RNGCHKFAIL
int3
; Total bytes of code: 46
Regression?
No response
Known Workarounds
Swap the operations or use unsafe code for the second access (which generates roughly the same ASM as the swapped version). This is obviously not a huge deal unless you are writing high-performance code without resorting to Unsafe
& friends.
Configuration
dotnet --version: 9.0.203
dotnet build -f net9.0 -c Release -o bin\Release\net9.0\Disasmo-v5.9.2
DOTNET_TieredPGO=0
DOTNET_JitDisasmDiffable=0
DOTNET_TieredCompilation=0
DOTNET_ReadyToRun=1
DOTNET_TieredPGO_InstrumentOnlyHotCode=0
Windows 10 64bit ZEN2
Can also be seen on https://godbolt.org/z/4q66bo36n and on arm64 arch as well
Other information
No response