JIT: unpin locals fed by non-GC typed values#130002
Conversation
Treat a non-GC typed def (eg a native int reinterpreted as a byref, as in a Span built from a pointer) as non-movable, so the pinned local can be unpinned. Fixes dotnet#129993. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
@EgorBo PTAL |
There was a problem hiding this comment.
Pull request overview
This PR updates the JIT’s “no-GC definition” classification used by pinned-local unpinning so that locals fed by certain non-GC-typed values can be treated as non-movable, enabling pin elision in more cases.
Changes:
- Expands
GenTree::IsNotGcDef()to early-returntruewhen the node’sTypeGet()is not GC-typed.
| // A non-GC typed value (eg native int reinterpreted as byref) cannot designate a movable object. | ||
| if (!varTypeIsGC(TypeGet())) | ||
| { | ||
| return true; | ||
| } |
| // A non-GC typed value (eg native int reinterpreted as byref) cannot designate a movable object. | ||
| if (!varTypeIsGC(TypeGet())) | ||
| { | ||
| return true; | ||
| } |
| bool IsNotGcDef() const | ||
| { | ||
| // A non-GC typed value (eg native int reinterpreted as byref) cannot designate a movable object. | ||
| if (!varTypeIsGC(TypeGet())) |
There was a problem hiding this comment.
I assume this sort of marks any untracked GC pointer as UB even if previously it worked, like
fixed (... = Unsafe.AsPointer(gcRef))
cc @dotnet/jit-contrib in case if you see any concern
There was a problem hiding this comment.
any concern
This is a breaking change. I don't see how it can be justified, since the pinned state of a value is a dynamic property. Here we only know that the value is pinned at the def point, but we need to know that it "would be" pinned for at least the lifetime the caller is concerned about.
There was a problem hiding this comment.
ECMA spec explicitly talks about "Start GC tracking" and "Stop GC tracking" for conversions.
There was a problem hiding this comment.
Yes this is too broad.
Seem like though that a non-gc parameter could possibly be handled here. If the parameter actually referred to GC memory whoever passed the argument would need to have pinned or they'd already be broken.
There was a problem hiding this comment.
If the parameter actually referred to GC memory whoever passed the argument would need to have pinned or they'd already be broken.
I don't think parameters are special in any way. The pinning state can be dynamically altered for them just as for normal variables (through opaque means such as calls, inter-thread sync, GC handles, aliased pointers, etc).
There was a problem hiding this comment.
It seems to me the analysis is also reliant on the "global" (or at least per-method) perspective, since a "true" return value is treated as representing a value that can't be made movable 'no matter what'. It would require a very sophisticated analysis indeed to prove that the pinned state of a value [that may be movable] doesn't change beyond its use in a def, in the general case.
There was a problem hiding this comment.
As others have seemingly already mentioned here 😅: #129993 (comment)
There was a problem hiding this comment.
The global analysis is flow insensitive so def vs use ordering shouldn't matter?
There was a problem hiding this comment.
The global analysis is flow insensitive so def vs use ordering shouldn't matter?
What I meant is basically this:
void* p = GetPin(); // 'p' pinned here via e. g. a handle, the virtual 'pin ref count' increases: 0 -> 1
pinned j = p; // It increases again: 1 -> 2
Unpin(p); // Decreases: 2 -> 1
// ...
// 'j's value still pinned here
// ...
return; // Decreases again: 1 -> 0, unpinned.as the situation where the pinned state (analogous to a ref count) "changes beyond its use in a def [here the j = p def]". We know that the value represented by p at the point of the def must be pinned [otherwise we have UB], but beyond that point it must be proven by some other means.
There was a problem hiding this comment.
Thanks all. Didn't appreciate that we have these non-lexical pinning mechanisms which makes any kind of local inference unsound.
|
@MihuBot -nuget |
Treat a non-GC typed def (eg a native int reinterpreted as a byref, as in a Span built from a pointer) as non-movable, so the pinned local can be unpinned. Fixes #129993.