-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Remove redundant pinning over static fields/RVA #119674
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
base: main
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HasGCStaticBase condition should not be needed for NativeAOT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks. Removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
| } | ||
|
|
||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function was moved from gentree.h and canOmitPinning is added.
Also I added gtPeelOffsets check
| TypeHandle fieldOwnerType(field->GetEnclosingMethodTable()); | ||
| if (!fieldOwnerType.GetLoaderAllocator()->CanUnload() && !fieldOwnerType.IsCanonicalSubtype()) | ||
| { | ||
| if (field->IsRVA()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (field->IsRVA()) | |
| if (field->IsRVA() || !field->IsByValue()) |
This can mirror the logic from CEEInfo::getFieldInfo
| else | ||
| { | ||
| GCX_COOP(); | ||
| void* pFieldAddr = field->GetCurrentStaticAddress(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| void* pFieldAddr = field->GetCurrentStaticAddress(); | |
| void* pAddr = field->GetStaticAddressHandle((void*)field->GetBase()); | |
| Object* frozenObj = VolatileLoad((Object**)pAddr); | |
| // ContainsGCPointers here is unnecessary but it's cheaper than IsInFrozenSegment | |
| // for structs containing gc handles | |
| if (!frozenObj->GetMethodTable()->ContainsGCPointers() && | |
| GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(frozenObj)) | |
| { | |
| result = true; | |
| } |
It is questionable to cast an interior pointer to Object* and pass it to the GC. It may happen to work currently but it does not sound like a good idea.
Instead, we should mirror the logic from getFieldInfo that does not have this problem,
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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/