-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[crossgen2] Promote single byref aot. #65682
Conversation
Tagging subscribers to this area: @JulieLeeMSFT Issue Detailsnull
|
sandreenko requested a review from MichalStrehovsky as a code owner now I did not :-) |
@trylek this is what we were discussing over emails, thanks again for the help, please take a look at VM changes. @jakobbotsch could you please tell me what runtime/src/coreclr/jit/morph.cpp Lines 7364 to 7368 in 3e01d11
|
Hmm, I will have to take a closer look, this predates me. |
/azp run runtime-coreclr outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
It looks like it was changed in c156e4b270cd1c01a6431a, before that it did not have the check for implicit byrefs. My guess is that the check was necessary before this as promotion of implicit byref parameters can introduce struct locals for which we may not have up-to-date address exposure information. However I do not see why the check should be necessary for parameters that are not implicit byref parameters. Unfortunately changing the checks around tailcalls give a lot of diffs with new tailcalls and many of them do not seem that profitable because we duplicate a lot of epilogs, so I'm not sure how easy it is to remove it. I tried some similar improvements in #65102. We probably need better profitability analysis of tailcalls before we start lifting restrictions. |
I see, thanks @jakobbotsch, I guess we can ignore this regressions for now given that they are small. -171k win sounds like a lot so maybe if you have ideas about other types that we can guess in a similar way feel free to add them. Of course the win is an indicator how expensive struct byref copy is and it wont be that impressive on primitive types, but maybe we can do the same for |
// It is important to struct promote managed value classes that have GC pointers | ||
// So we compute the correct value for "CustomLayout" here | ||
// | ||
if (StructHasCustomLayout(typeFlags) && ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0)) |
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 am not sure if we can include CORINFO_FLG_BYREF_LIKE
here. It goes to this code
/// This MethodTable is for a Byref-like class (TypedReference, Span<T>,...) |
@MichalStrehovsky do you know if we can promote these classes as a single byref?
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.
Both TypedReference and Span have multiple fields - I don't know how much struct promotion helps for that. There's a C# feature in the works to support ref fields; the runtime already supports it. It allows for ref struct MyStruct { ref int RefForInt; }
. Maybe it would be beneficial for that, but I don't know how common that pattern will be.
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
@dotnet/jit-contrib |
/azp run runtime-coreclr crossgen2 outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
Looks like there are a lot of CG2 failures, but a lot of them look like infra issues (e.g. the |
Sure, when I have time. I see many logs but can't find the one that shows how to repro this test locally, which env variable to set and which command to run, do you now how to find it? Most logs start with a preexisting rsp file. |
I could not repro it locally with the wrapper script and setting There are still a lot of other presumably unrelated failures. I want to retry running the leg, can you rebase the PR and I will kick it off again? |
b8d5a05
to
b2758a0
Compare
/azp run runtime-coreclr crossgen2 outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
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.
LGTM, thank you! I have added two comments but they're mostly aimed at the Crossgen2 team, not direct requests for you to change anything in your PR (except for the suggested code comment, that might be useful especially as this logic is still apparently somewhat fragile).
return false; | ||
} | ||
|
||
const unsigned structSize = compHandle->getClassSize(typeHnd); |
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 is probably worth a comment as this exact line has key importance for version resiliency. In the JIT interface in CorInfoImpl
, the method getClassSize
is the one that implicitly triggers creation of the ENCODE_TYPE_LAYOUT_CHECK
fixup that is in turn required to invalidate the pre-jitted code when the shape of the struct in a separately versioned module changes (say, when in your example someone changes the type of the o
field from Object to IntPtr or adds a new field n
before the field o
without recompiling the code in which the promotion has taken place).
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.
Thanks @trylek.
Just to confirm, lets say we have
struct OuterStruct {
InnerClass innerField;
}
I can't think of a scenario where before my changes we did not have a ENCODE_TYPE_LAYOUT_CHECK
for OuterStruct
and after my changes we have them. We also don't have a check for InnerClass
before and after the changes. If the jit was working with OuterStruct
it would have to know its size and ask if there are byref fields for all operations: initialization, copying, passing as arguments and returning. So if we see a struct the jit will always ask these questions so asking them here does not change anything.
There could be a scenario where we prove that the struct is unused and can be deleted but I believe it happens during liveness when all these questions are already asked to create right copy/init tree nodes.
So I don't think that this line is important and the comment only here would be confusing for me, like why all other places where we call getClassSize
don't have such comment.
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.
OK, you have convinced me that the code comment I suggested wouldn't have much value. I guess I'm mostly somewhat concerned about the fact that getClassAttrib
doesn't emit the type layout check, I'd love to hear from David whether that's intentional (there's no reasonable scenario where JIT would only ask about the attribs and not size), opportunistic (perhaps an additional place emitting the fixup might somewhat slow down the compilation) or just an omission. One way or another, I have already approved your change and on my part I don't see anything preventing it from being merged in.
{ | ||
const CORINFO_CLASS_HANDLE typeHnd = structPromotionInfo->typeHnd; | ||
const COMP_HANDLE compHandle = compiler->info.compCompHnd; | ||
const DWORD typeFlags = compHandle->getClassAttribs(typeHnd); |
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.
Strictly speaking getClassAttribs
should probably also trigger emission of the type layout check - if JIT queried this information and made some codegen decisions based on it, the generated code should be also invalidate when the class attributes change. I believe it's not happening today and it may be the case that all "important" places asking for getClassAttribs
ultimately also need to query getClassSize
but it's still pretty fragile. Adding @davidwrighton to hear his thoughts on the subject.
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.
LGTM, very nice diffs! The new CG2 outerloop run looks to have the same set of failures as other runs so will merge this.
This reverts commit c6ca9dc.
* Rename `CORINFO_FLG_DONT_PROMOTE` to `CORINFO_FLG_DONT_DIG_FIELDS`. * Support promotion of `struct{ 1 gcref; }` outside of version bubble.
Allow crossgen2 jit to promote structs like:
even when we can't ask about 'o' during crossgen2 compilation.
Some positive diffs:
the wins look as expected, for example,
Microsoft.CodeAnalysis.VisualBasic.Symbols.OverriddenMembersResult
1:.ctor(System.Collections.Immutable.ImmutableArray1[System.__Canon],System.Collections.Immutable.ImmutableArray
1[System.__Canon],System.Collections.Immutable.ImmutableArray1[System.__Canon]):this (MethodHash=18164335)
:before:
after:
the regression are caused by some tail calls that we now reject because of this condition:
runtime/src/coreclr/jit/morph.cpp
Line 7364 in 3e01d11