-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use *_NOCTOR jit helpers version once class is initialized #75785
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsToday, we have A real world example of such code is https://github.com/microsoft/FASTER/blob/a3aafc8cae8298267ded2e884aea4377d69d0e77/cs/src/core/Epochs/LightEpoch.cs#L526-L536 Here, Here is the simple repro: [ThreadStatic]
static ushort startOffset1;
static int Test(int n) {
int result = 0;
for (int i = 0; i < n; i++) {
result += startOffset1;
}
return result;
} G_M40168_IG03:
mov rcx, 0xD1FFAB1E
mov edx, 9
call CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE
movzx rax, word ptr [rax+28H]
add edi, eax
inc ebx
cmp ebx, esi
jl SHORT G_M40168_IG03 However, if we know that the class is already initialized, we can use the G_M40168_IG02:
xor edi, edi
xor ebx, ebx
test esi, esi
jle SHORT G_M40168_IG04
mov rcx, 0xD1FFAB1E
mov edx, 9
call CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR
movzx rax, word ptr [rax+28H]
;; size=32 bbWeight=1 PerfScore 5.25
G_M40168_IG03:
add edi, eax
inc ebx
cmp ebx, esi
jl SHORT G_M40168_IG03
|
Could the |
Could you please share an example of what you have in mind? |
I'm talking about this check, couldn't it check whether the type is managed, not just a struct? runtime/src/coreclr/vm/jitinterface.cpp Line 1265 in 38e3518
|
It would mean redoing the whole static field layout algorithm in the type loader. It can be done, but it would be pretty non-trivial change and comes with trade-offs (like slower assembly and type loading). |
Ah, I wasn't sure whether the info is available at this point, if not then changing it here doesn't make sense. |
Today, we have
*_NOCTOR
helpers and default helpers for static class initialization. They are used to find the base address of various statics and thread statics. The*_NOCTOR
versions are usually hoistable friendly because we know there is no static ctor to execute at any point and the static access can be safely moved around. However, for classes having static ctors, we use the default helpers (the one that doesn't end with_NOCTOR
) and they do not get hoisted out of the loop.A real world example of such code is https://github.com/microsoft/FASTER/blob/a3aafc8cae8298267ded2e884aea4377d69d0e77/cs/src/core/Epochs/LightEpoch.cs#L526-L536
Here,
ReserveEntry()
has bunch of access to thread statics inside a loop and is part of a class that has static ctor. We end up calling helperCORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE
(which is expensive) inside the loop since we do not hoist it outside the loop because of the possibility of point where static ctor should be executed.Here is the simple repro:
However, if we know that the class is already initialized, we can use the
NOCTOR
version of the helper in subsequent JITs and optimize the helper call out of the loop.