Description
This is a follow up from (RIP) #55273, specifically this comment from @jkotas.
Opening this issue for tracking and avoid that getting lost.
Overview
Consider this snippet:
static unsafe void A(ref byte r)
{
fixed (void* p = &r)
{
B(p);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static unsafe void B(void* p)
{
}
This currently results in:
sub rsp, 0x28
mov [rsp+0x20], rcx
call 0x00007ffaecb70028
xor eax, eax
mov [rsp+0x20], rax
add rsp, 0x28
ret
Currently, all pinned locals are always stored on the stack. This makes pinning not really ideal for hot paths.
It would be nice if the JIT added support for using a register to store pinned locals, when possible.
As mentioned by @tannergooding, the register would need to be cleared when out of scope to stop tracking.
The method A
from above could then become something like this:
mov rbx, rcx
call 0x00007ffaecb70028
xor rbx, rbx
ret
Here I just used rbx
to store the pinned local (just picked the first callee-saved register). I do realize there's plenty of work to make this work and all the various GC data structures need to be updated accordingly to enable tracking, this is the general idea.
Goes without saying that this optimization could bring some nice codegen/perf wins in interop-heavy code 😄
Additionally, given this could be used to restore the RuntimeHelpers.GetHashCode
optimization by porting the happy path to C# (as I did in #55273, but possibly without the GC hole ahah), it would automatically speedup virtually every dictionary out there using random reference types as keys. Or, any other data structure that would call GetHashCode
at some point on an object that didn't override the default object.GetHashCode
implementation.
category:cq
theme:pinning