Skip to content
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

Reduce duplicity in GVM analysis #83250

Merged
merged 4 commits into from
Mar 14, 2023
Merged

Conversation

MichalStrehovsky
Copy link
Member

There were two very similar pieces of logic in the generic virtual method analysis. First one was deciding whether we need to emit GVM bookkeeping about the type, the second one was deciding whether the type should participate in GVM analysis (the N*M algorithm, "for each type relevant to GVM analysis" * "GVM virtual method called").

Made this into one so that it's in one spot. The interface method resolution is not cheap so this also speeds up the compilation. We should also do this for non-generic virtuals at some point.

I did a test pass with an assert that the analysis came up with the same conclusions to ensure I didn't mess up.

Cc @dotnet/ilc-contrib

There were two very similar pieces of logic in the generic virtual method analysis. First one was deciding whether we need to emit GVM bookkeeping about the type, the second one was deciding whether the type should participate in GVM analysis (the N*M algorithm, "for each type relevant to GVM analysis" * "GVM virtual method called").

Made this into one so that it's in one spot. The interface method resolution is not cheap so this also speeds up the compilation. We should also do this for non-generic virtuals at some point.

I did a test pass with an assert that the analysis came up with the same conclusions to ensure I didn't mess up.
@ghost
Copy link

ghost commented Mar 10, 2023

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

There were two very similar pieces of logic in the generic virtual method analysis. First one was deciding whether we need to emit GVM bookkeeping about the type, the second one was deciding whether the type should participate in GVM analysis (the N*M algorithm, "for each type relevant to GVM analysis" * "GVM virtual method called").

Made this into one so that it's in one spot. The interface method resolution is not cheap so this also speeds up the compilation. We should also do this for non-generic virtuals at some point.

I did a test pass with an assert that the analysis came up with the same conclusions to ensure I didn't mess up.

Cc @dotnet/ilc-contrib

Author: MichalStrehovsky
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: -

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).


[Flags]
protected enum VirtualMethodAnalysisFlags
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to use the 0 value I would give it a name, like "None"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add it, but generally disagree - None is not a flag. Having a named value for it makes it non-obvious whether we're setting a flag or if this means no flag is set. We have actual non-zero flags named None even in our own CoreLib.

// We found a GVM on one of the implemented interfaces. Find if the type implements this method.
// (Note, do this comparison against the generic definition of the method, not the specific method instantiation
MethodDesc slotDecl = method.Signature.IsStatic ?
defType.ResolveInterfaceMethodToStaticVirtualMethodOnType(method)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have two different methods here? Could we simplify to just ResolveInterfaceMethodTarget that could handle static and non-static members?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to simplify to one method once all type system consumers can deal with static methods. crossgen2 currently still doesn't handle static virtuals and I'm not sure if changing this could break it.

(This is a copy from one of the places that I'm unifying - not new code).

// these all will be looked at for every unique generic virtual method call in the program.
// Having a long list of interesting types affects the compilation throughput heavily.
if (slotDecl.OwningType == defType ||
defType.BaseType.ResolveInterfaceMethodTarget(method) != slotDecl)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's an example of when this could happen?

My understanding of the code is that it's checking to see if the impl of the interface in the base type is different, but only when the current impl isn't declared in the current type. That would seem to imply to me that the impl is somehow being pulled from above the base type, which seems impossible at first glance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example is a twist on the sample 30 lines above.

            //      interface IFace {
            //          void IFaceGVMethod<U>();
            //      }
            //      class BaseClass {
            //          public virtual void IFaceGVMethod<U>() { ... }
            //      }
            //      public class DerivedClass : BaseClass, IFace { }
            //

Move the IFace implementation down to BaseClass. Now if we try to resolve IFaceGVMethod on DerivedClass we get a method from the base, but it already implemented the interface in the base. So we don't have to think about it in the context of DerivedClass. Removing DerivedClass from the N*M algorithm is a compile throughput improvement.

(This is a copy from one of the places that I'm unifying - not new code).

Copy link
Member

@agocke agocke Mar 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, still not getting it. If the example code is

            //      interface IFace {
            //          void IFaceGVMethod<U>();
            //      }
            //      class BaseClass : IFace {
            //          public virtual void IFaceGVMethod<U>() { ... }
            //      }
            //      public class DerivedClass : BaseClass { }
            //

I would have assumed that slotDecl.OwningType would be BaseClass, and defType would be BaseClass. So we wouldn't execute the or condition. Is slotDecl the implementing slot?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defType would be DerivedClass.

We go over all interfaces on DerivedClass. For each interface method, we resolve to the implementation. If the implementation is something else than it was on the base, we need to track it. If the implementation is the same thing that it was on the base, we don't need to track it.

The if (slotDecl.OwningType == defType) part of the check is just a perf optimization to avoid going to the resolve step on base. If the implementation is on the type we're looking at right now, for sure the base type wouldn't have that.

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky MichalStrehovsky merged commit 55a0940 into dotnet:main Mar 14, 2023
@MichalStrehovsky MichalStrehovsky deleted the dedup branch March 14, 2023 05:32
@ghost ghost locked as resolved and limited conversation to collaborators Apr 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants