Uh oh!
There was an error while loading. Please reload this page.
[JIT] Add support to inline the field access of primitive types marked with TLS - #82973
Conversation
…peIndex in t_threadStaticBlocks
ghost
commented
Mar 4, 2023
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak Issue DetailsSummaryThis is an early prototype to add support of inlining A thread local cache During codegen, the enclosing type of the TS field that is being access (load or store) is monitored and gets a unique index. This index is the position at which the static data block will be stored in The To access the DisassemblyTo understand how it works, consider the following C# code. [MethodImpl(MethodImplOptions.NoInlining)]publicintGetThreadStaticInt()=>t_threadStaticIntValue;[ThreadStatic]privatestaticintt_threadStaticIntValue=0;Today, we always generate a helper call to retrieve the G_M000_IG01: ;; offset=0000H 4883EC28 subrsp,40G_M000_IG02: ;; offset=0004H 48B9D0482999FA7F0000 movrcx,0x7FFA992948D0 BA78010000 movedx,376 E81894A65F call CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR 8B80A4040000 moveax, dword ptr [rax+04A4H]G_M000_IG03: ;; offset=001EH 4883C428 addrsp,40 C3 retWith this prototype, this is what we would generate: G_M000_IG01: ;; offset=0000H56pushrsi 4883EC20 subrsp,32G_M000_IG02: ;; offset=0005H 65488B0C2558000000 movrcx, qword ptr GS:[0x0058] ; Access the TLS of current thread 488B7130 movrsi, qword ptr [rcx+30H] ; Get the runtime TLS slot from TLS[_tls_index] 4883BEB801000002 cmp qword ptr [rsi+1B8H],2 ; See if length of `t_threadStaticBlocks` > typeIndex. Here `typeIndex == 2`. 7E22 jle SHORT G_M000_IG04 ; If yes, then proceed, else fallback to the helperG_M000_IG03: ;; offset=001CH 48B9D048F522FA7F0000 movrcx,0x7FFA22F548D0 BA78010000 movedx,376 41B802000000 movr8d,2 ; This is a new paramter to the helper that would cache static data block in `t_threadStaticBlocks` at index `2`. E8FAF8AB5F call CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR 8B80A4040000 moveax, dword ptr [rax+04A4H] EB16 jmp SHORT G_M000_IG06G_M000_IG04: ;; offset=003EH 488B86B0010000 movrax, qword ptr [rsi+1B0H] ; Get the `t_threadStaticBlocks[typeIndex]`. 488B4010 movrax, qword ptr [rax+10H] ; Get the `t_threadStaticBlocks[typeIndex]`. 4885C0 testrax,rax ; Check if a valid entry is present at `t_threadStaticBlocks[typeIndex]`. 74CE je SHORT G_M000_IG03 ; If invalid, then go to the helperG_M000_IG05: ;; offset=004EH 8B80A4040000 moveax, dword ptr [rax+04A4H] ; If valid, then get the field value.G_M000_IG06: ;; offset=0054H 4883C420 addrsp,32 5E poprsi C3 retDetailsTODO TODO
Contributes to #79521.
|
@kunalspathak, can you also give an example of the use of a For example, what is the codegen given the following: [ThreadStatic]publicstaticvolatileintt_value;publicstaticintTest(uintcount){intsum=0;for(uinti=0;i<count;i++){sum+=t_value;}returnsum;}I'd expect we end up with the large up front block as "hoistable", that is the initial resolution of the TLS base. The inner loop should then remain "small" and effectively just a direct memory access since we'll have already resolved the base/offset of the TLS value for the given thread. Notably it also looks like there is a "cheap check" and "expensive fallback" to the TLS handling here. The way the blocks are being ordered doesn't look to mark the expensive fallback as "cold" which might negatively impact things, particularly for subsequent executions of the code. |
kunalspathak
commented
Apr 10, 2023
Failures are known issues. |
BruceForstall
left a comment
There was a problem hiding this comment.
Just a couple hopefully minor questions/comments
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
EgorBo
commented
Apr 10, 2023
LGTM with a couple of nits |
…nullptr 1st time and valid value 2nd time onwards
kunalspathak
commented
Apr 11, 2023
Other failures are unrelated and fixed by #84649 |
Summary
This is an early prototype to add support of inlining
ThreadStatic(TS) field access (for now just the primitive types. Today, for fields that are marked withThreadStaticalways has to go through the helper. The helper first gets the current thread, the thread local block, then the ThreadLocalModule for the current moduleIndex and lastly the static data block. This prototype tries to inline such accesses by adding few data structures that acts as a cache.A thread local cache
t_threadStaticBlocks(array of pointers) is added which stores the static blocks corresponding to the given thread.During codegen, the enclosing type of the TS field that is being access (load or store) is monitored and gets a unique index. This index is the position at which the static data block will be stored in
t_threadStaticBlockscache at runtime. Since the index is assigned during codegen and embedded in the code, it remains same for any thread that executes the code at runtime. Hence any thread that executes the code, will make sure that it gets the relevant static data block fromt_threadStaticBlockscache.The
t_threadStaticBlocksis populated during runtime as well. The first time the field access code is executed, it tries to find the static data block int_threadStaticBlocksbut doesn't find it. It fallbacks to the slow path which is the existing helper call. The helper call has been modified to update thet_threadStaticBlockswith the static data block. Next time when the TS field access code is executed, the entry is found in the cache, and we skip going to the helpers.To access the
t_threadStaticBlocksat runtime, code is generated to access the relevant cache for the current thread by fetching theTLSof current thread, getting the slot for runtime, and then getting thet_threadStaticBlockspresent in that slot.Disassembly
To understand how it works, consider the following C# code.
Today, we always generate a helper call to retrieve the
t_threadStaticIntValuefield value.With this prototype, this is what we would generate:
Details
TODO
TODO
t_threadStaticBlocks.typeIndex.Contributes to #79521.