-
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
Remove ref struct pinning from locals #97997
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsRemoves pinned from
|
@@ -275,7 +275,26 @@ void Compiler::lvaInitTypeRef() | |||
|
|||
if ((corInfoTypeWithMod & CORINFO_TYPE_MOD_PINNED) != 0) | |||
{ | |||
if ((corInfoType == CORINFO_TYPE_CLASS) || (corInfoType == CORINFO_TYPE_BYREF)) | |||
CORINFO_CLASS_HANDLE refClsHnd = NO_CLASS_HANDLE; | |||
// don't run in crossgen to avoid invalid type info |
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.
What is the invalid type info problem with crossgen?
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.
R2R could be compiled against an assembly version where the type was flagged as byref-like while it was changed to a normal struct later.
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.
Changing byref-like struct to non-byref like struct is not a compatible change. Once the struct is byref-like in public surface, it has to stay byref-like.
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.
Even if so, it's safer to just skip running it there.
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.
We want AOT compilers to have same optimizations as JIT where possible.
if ((refClsHnd != NO_CLASS_HANDLE) && | ||
((info.compCompHnd->getClassAttribs(refClsHnd) & CORINFO_FLG_BYREF_LIKE) != 0)) | ||
{ | ||
JITDUMP("Ignoring pinned on local V%02u for byref-like type %s\n", varNum, eeGetClassName(refClsHnd)); |
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.
@jkotas We were having a discussion on Discord whether enforcing ref struct
ref
s not pointing to the heap is an acceptable assumption for the JIT to make, could you give your opinion on the safety of this change?
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.
It is fine to assume that ref struct
is pinned and allocated on the stack. It would be a major breaking change (for the R2R format at least) to allocate ref struct
s on the GC heap.
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.
The concern is about aliasing. Before this change the JIT's aliasing model allows roundtripping any ref A
as a ref B
. The optimization in this PR is not valid unless we consider that to be UB when A and B do not match in byref likeness.
Diff results for #97997Assembly diffsAssembly diffs for osx/arm64 ran on windows/x64Diffs are based on 1,731,165 contexts (559,643 MinOpts, 1,171,522 FullOpts). MISSED contexts: base: 227 (0.01%), diff: 5,356 (0.31%) Overall (-12 bytes)
FullOpts (-12 bytes)
Details here Throughput diffsThroughput diffs for windows/arm64 ran on windows/x64MinOpts (-0.00% to +0.01%)
Details here |
Just curious - what are the savings here? Pinning references that happen to be stack refs seems like mostly a noop - basically just a different bit in GCinfo with no effect on GC. Once GC figures that byref does not point to the heap (which it needs to figure regardless), it will not care about pinning. Actually will stop caring about the reference entirely. Are there some possible savings on the JIT side? |
The motivation was to improve the codegen a bit as some people use such pinning to workaround the lack of |
I see. That is basically because pinned locals are currently never enregistered, so pinning will force a local to the stack. |
Does this also ignores pinning for fields of ref struct types (pointed to by ref)? Eg.: fixed (long* p = &r2.a)
{
} (using the same types and names as that snippet in #97997 (comment)) |
No as the local is not a ref to a ref struct in such case. |
Not sure I'm following. What I meant is, if you have: ref struct MyRefStruct
{
public int X;
}
void Test(ref MyRefStruct value)
{
fixed (int* p = &value.X)
{
DoStuff(p);
}
} Shouldn't it also be possible to ignore the pinning on that |
The optimization here is done before the JIT even looks at the IL opcodes so it doesn't know what's assigned to the local, the case you've mentioned would need to be handled later on by looking at the struct layout the |
Unfortunately I don't think the impact of the optimization warrants us changing the model of what kind of byref aliasing we consider legal within the JIT. If we could demonstrate some larger impact, then I wouldn't be against reconsidering. Thanks! |
Removes pinned from
ref struct
locals as refs to them can't point to the heap legally.