Description
When a ReadOnlySpan<byte>
is initialized from an RVA static, typical bounds check elision on the span's indexer doesn't take place as expected.
private static ReadOnlySpan<byte> MyRvaStatic => new byte[] { 1, 2, 3, 4, 5 };
public static int FetchFromRvaStatic(int index) {
int retVal = default;
ReadOnlySpan<Byte> span = MyRvaStatic;
if ((uint)index < (uint)span.Length)
{
retVal = span[index];
}
return retVal;
}
public static int FetchFromArgument(int index, ReadOnlySpan<byte> span) {
int retVal = default;
if ((uint)index < (uint)span.Length)
{
retVal = span[index];
}
return retVal;
}
C.get_MyRvaStatic()
L0000: mov rax, 0x2a875b3099c
L000a: mov [rcx], rax
L000d: mov dword [rcx+0x8], 0x5
L0014: mov rax, rcx
L0017: ret
C.FetchFromRvaStatic(Int32)
L0000: sub rsp, 0x28
L0004: xor eax, eax
L0006: cmp ecx, 0x5
L0009: jae L0021
L000b: cmp ecx, 0x5
L000e: jae L0026
L0010: movsxd rax, ecx
L0013: mov rdx, 0x2a875b3099c
L001d: movzx eax, byte [rax+rdx]
L0021: add rsp, 0x28
L0025: ret
L0026: call 0x7ff9de711e00
L002b: int3
C.FetchFromArgument(Int32, System.ReadOnlySpan`1<Byte>)
L0000: mov rax, [rdx]
L0003: mov edx, [rdx+0x8]
L0006: xor r8d, r8d
L0009: cmp ecx, edx
L000b: jae L0015
L000d: movsxd r8, ecx
L0010: movzx r8d, byte [rax+r8]
L0015: mov eax, r8d
L0018: ret
Note the FetchFromRvaStatic
method has two bounds checks: the manual bounds check I wrote and the auto-generated bounds check from the indexer. The FetchFromArgument
method only has the manual bounds check; the auto-generated bounds check is elided.
category:cq
theme:optimization
skill-level:expert
cost:medium
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment