-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Improve resolveVirtualMethodHelper for generic interfaces #56254
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
Conversation
|
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
|
jit-diff (pmi, separate runs + jit-analyze): Diff examples: But the impact should be way bigger in FullPGO mode |
|
Another example: using System;
using System.Collections.Generic;
using System.Threading;
public class Program
{
public static void Main()
{
for (int i = 0; i < 100; i++)
{
Test(new List<string> {"1", "2"});
Thread.Sleep(16);
}
}
static void Test(IEnumerable<object> enumerable)
{
foreach (object item in enumerable)
Console.WriteLine(item);
}
}Codegen diff for (GetEnumerator, get_Current are devirtualized, but not inlined - it looks like we don't update generic context for guarded call-sites, minimal repro https://gist.github.com/EgorBo/03fbcc79a6dc5fac8534016803676011 - |
|
Does crossgen2 need the same fix, or does it work fine already? |
@jkotas it seems it does already, here is the codegen for ^ it contains a GDV fast path for |
|
From my understanding, this change (similar to mine) in crossgen2 is responsible for that: https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs#L1190-L1198 |
|
Changed the code to align with crossgen2, jit-diff is the same. |
|
I worry that this check is too simplistic ... if the base class for the method being invoke is a shared interface class, it seems like we need to use the owning context to determine the exact base class. And I don't know if we can make that determination properly with just class handles; it might require looking at signatures. What this change does is use the owning context as the exact base class. Seems like that could be inaccurate if the type dependence of the interface class is more complex than just a single directly used type parameter. |
|
Initially the change was like this c50eb8d so it first tried the original base class, then it tried the generic context (also, it ignored cases where current object is shared instantiation). |
davidwrighton
left a 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.
The analysis that @EgorBo did around the behavior of CanCastToInterface looks correct to me. That should weed out the cases this can't reliably handle at this time. Well, the CanCastToInterface is needed as is the weeding out with canonically equivalent interfaces, but since both of those checks are in place, this should work. With the signature checks that @AndyAyersMS suggests, there are some cases which could be permitted even if the CanCastToInterface check will fail, but I really don't think we should invest in trying to make that work at this time.
|
David, thanks for double-checking. |
|
Merging to 7.0 |
Example (@stephentoub's repro)
Currently we give up on devirtualizing
GetIList()call inTestmethod despite the fact we have everything we need to do so:namely:
The problem is in VM's
resolveVirtualMethodHelperthat checks thatList<Program>.CanCastToInterface(IList<__Canon>)is false and gives up - doesn't try to use the provided generic context.Codegen for
TestBefore and After: https://www.diffchecker.com/GUi21Vt0/cc @AndyAyersMS