Remove redundant pinning over static fields/RVA - #119674
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
| private bool canOmitPinning(CORINFO_FIELD_STRUCT_* fldHnd) | ||
| { | ||
| FieldDesc field = HandleToObject(fldHnd); | ||
| if (!field.IsStatic || field.IsThreadStatic || field.HasGCStaticBase || field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) |
There was a problem hiding this comment.
HasGCStaticBase condition should not be needed for NativeAOT.
There was a problem hiding this comment.
HasGCStaticBase condition is needed for R2R to be functionally correct. Are we seeing any crashes in GCStress+R2R with this change? If not, we may consider adding some targeted tests to validate that this optimization is valid.
It is an interesting question how conservative the contract should be for R2R. I guess we can start with the condition you had here originally. It will be one more reason why R2R is incompatible and ignored for collectible ALCs.
|
@jkotas could you please review the VM side? I guess I have a few conservative checks like ThreadStatics, but I was mostly focusing on RVA fields and structs (boxed statics) |
There was a problem hiding this comment.
Pull Request Overview
This PR optimizes pinning operations in the JIT compiler by introducing a new canOmitPinning API that allows the JIT to skip pinning overhead when working with static fields that are guaranteed to be stable in memory. The optimization applies to RVA fields, primitive static fields in unmanaged memory, and struct static fields stored on the NonGC heap.
Key changes:
- Introduces
canOmitPinningJIT interface method to determine when pinning can be safely omitted - Enhances
IsNotGcDefto consider field sequences and call the new API for static fields - Updates SuperPMI infrastructure to support the new interface method
Reviewed Changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/jitinterface.cpp | Implements canOmitPinning with logic to check if static fields are safe to use without pinning |
| src/coreclr/jit/gentree.cpp | Refactors IsNotGcDef to accept compiler parameter and check static field sequences |
| src/coreclr/jit/gentree.h | Changes IsNotGcDef signature to require compiler parameter |
| src/coreclr/jit/lclvars.cpp | Updates call to IsNotGcDef with compiler parameter |
| src/coreclr/inc/corinfo.h | Adds canOmitPinning method declaration to JIT interface |
| src/coreclr/inc/jiteeversionguid.h | Updates JIT-EE version identifier for interface change |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Implements simplified AOT version of canOmitPinning |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs | Auto-generated code for new interface method |
| Various SuperPMI files | Adds infrastructure support for recording and replaying canOmitPinning calls |
| private bool canOmitPinning(CORINFO_FIELD_STRUCT_* fldHnd) | ||
| { | ||
| FieldDesc field = HandleToObject(fldHnd); | ||
| if (!field.IsStatic || field.IsThreadStatic || field.HasGCStaticBase || field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) |
There was a problem hiding this comment.
HasGCStaticBase condition is needed for R2R to be functionally correct. Are we seeing any crashes in GCStress+R2R with this change? If not, we may consider adding some targeted tests to validate that this optimization is valid.
It is an interesting question how conservative the contract should be for R2R. I guess we can start with the condition you had here originally. It will be one more reason why R2R is incompatible and ignored for collectible ALCs.
Remove the GTF_ICON_STATIC_HDL arm of IsNotGcDef. As @EgorBo noted on dotnet#129110, the static-data address can live in a collectible ALC's loader heap, where pinning may be what's keeping the LoaderAllocator reachable. The safe answer is the canOmitPinning JIT-EE API in dotnet#119674, which asks the VM to verify !LoaderAllocator->CanUnload(). The stackalloc fix in PHASE_UNPIN_LOCALS is unaffected: it still picks up the GT_LCL_ADDR path that was already in IsNotGcDef. SPMI asmdiffs loses only ~3K bytes out of -190K from this revert. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the JIT-EE conflicts, incorporate review feedback, and add JIT, ReadyToRun, NativeAOT, and GC stress coverage for static pinning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6626f902-a2c1-4f01-a35b-0981b4dcc0e7
|
@MihuBot -nuget |
# Conflicts: # src/coreclr/inc/corinfo.h # src/coreclr/inc/icorjitinfoimpl_generated.h # src/coreclr/inc/jiteeversionguid.h # src/coreclr/jit/ICorJitInfo_names_generated.h # src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp # src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs # src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt # src/coreclr/tools/aot/jitinterface/jitinterface_generated.h # src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h # src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h # src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp # src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
This comment was marked as spam.
This comment was marked as spam.
|
@jkotas @MichalStrehovsky anything else here on the VM side? |
MichalStrehovsky
left a comment
There was a problem hiding this comment.
The native AOT part looks good to me.
I don't know enough about R2R/CoreCLR VM to sign off on that part. My question marks for R2R/VM:
- do we have guarantees the R2R module is not loaded into unloadable ALC?
- it's not clear to me under what condition the statics are immovable. per #129664 CoreCLR will not create an immovable static for primitive types even if asked nicely, but here we seem to be saying canOmitPinning is true for them.
| void* address | ||
| ) = 0; | ||
|
|
||
| // Returns true iff pinning of the field's address can be elided because runtime guarantees stability. |
There was a problem hiding this comment.
| // Returns true iff pinning of the field's address can be elided because runtime guarantees stability. | |
| // Returns true iff pinning of the field's address can be elided because runtime guarantees the field | |
| // address won't move and the field storage won't be collected. |
Be more specific about what this actually guarantees
|
|
||
| return false; | ||
| } | ||
| bool IsNotGcDef(Compiler* comp) const; |
There was a problem hiding this comment.
Add a comment about what this returns?
My understanding we do not support R2R when we load code into an unloadable ALC today. if there is an API I can call to bail out unloadble ALC in R2R - I can call, but I don't see any. I have a suspicion (not based on anything) we have a lot of challenges to solve to enable r2r for that. For CoreCLR we bail out via
I am not sure I fully understand the issue, my understanding that they're always allocated on non-gc memory (nongc primitives). NonGC non-primitives are typically allocated on FOH (boxed statics). |
Right.
Known issues would not be that hard to solve. The testing/reliability (finding all issues) is the harder part.
Primitive statics are movable only in collectible assemblies in the current main. |
|
|
||
| #if READYTORUN | ||
| // RVA data and reference-type static slots are always stable. Keep value-type GC | ||
| // statics conservative because ReadyToRun can store them in movable boxes. |
There was a problem hiding this comment.
| // statics conservative because ReadyToRun can store them in movable boxes. | |
| // statics conservative because the runtime can store them in movable boxes. |
|
The VM side looks fine to me. Speaking about pinning, I wish for #63397. It should be able to produce similar codegen diffs in a lot more cases. |
I noticed that sometimes developers make assumptions that it's fine to use
Unsafe.AsPointerover RVA fields since they're not movable, which is fairly unreliable thing to do (e.g. Unloadable ALCs). So let's just optimize the pinning overhead in JIT when we know it's safe to do so:Codegen diff: https://www.diffchecker.com/LgIALnAn/