-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Only R2R public methods, methods that override public methods, and internal methods that aren't always inlined #75793
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
Only R2R public methods, methods that override public methods, and internal methods that aren't always inlined #75793
Conversation
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.
Nice... but I think the recompile tech is the wrong hook to use here.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/MarshalHelpers.ReadyToRun.cs
Outdated
Show resolved
Hide resolved
|
I think a better way to do this would be to compile these stubs, but do not save them into the final image if we find that they are not referenced from anywhere. It would address the problem of JIT deciding to not inline the stub in cold code, etc. Similar optimization can be applied to all inlineable method (the same concerns about public or InternalVisible apply). The blittable PInvoke stubs are really just a special-case of inlineable methods. |
|
@jkotas, the right way to do that would be to use the |
|
References to PInvoke stubs are relocs. Is there a reason for not using reloc dependencies for this? I agree that you can make it work using |
If these show up as relocations within the produced method code, it would definitely be worth investigating why it doesn't "just work" automatically (it should be requesting a method body for the p/invoke and none of the extra code should be needed to handle that). |
crossgen2 adds all methods as roots: runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs Lines 79 to 128 in 57bfe47
|
|
I've validated that the behavior is identical to what I had before if I just don't root P/Invokes, so I've removed the JitInterface changes. |
|
I did a prototype of only rooting methods that are visible outside of the assembly and found that we can save probably an additional 300kb from an additional 6k methods or so that are always inlined. This doesn't account for any rooting of the internal/private methods called by the runtime, so I'd need to port that infrastructure over from ILC (as we already support the .NET Linker rooting format there, and we already generate a file of that format as part of the CoreLib build for these exact methods) to get exact numbers. I've attached a copy of the diff against this PR that I got in my prototype. I'm a little suspicious of some of the results, so I definitely need to do more work if we want to go in this direction (in addition to rooting method called from CoreLib). |
|
I've updated this PR to be more generic and only root methods that may be exposed externally instead of only skipping P/Invokes. Can I get another review pass? |
|
Is this statement correct? "Reflection invoke on private methods will fall back to JIT unless the method is rooted in some other way (e.g. called from somewhere else)." The diff you have shared is surprising. If I read it correctly, methods like |
Yes, that statement is correct. If we want to make that statement incorrect (ie track reflection), we could import more of the linker compatibility code from ILC to assist in reflection analysis.
I need to figure that out. I'm seeing quite a few interesting methods in that list that all seem to match the following description:
Do you have any ideas why methods in this case wouldn't get dependency nodes added? Many of the methods (like |
It may a problem with tailcall optimization confusing the dependency tracker. |
|
That would make sense. I'll see if I can trace down what's happening there. |
|
I think the tailcalls might not use the reloc's mechanism and as a result are being missed. @davidwrighton mentioned some other dependency mechanism in ILC above that might be good for this. We may have to update the R2R side of the JIT interface after all to add these dependencies during code-generation. I'll give it a shot. |
|
The tailcalls targets are relocs as well. My guess is that the type of the reloc for tailcall is different from a regular call and not understood by the dependency tracker, but it is still a reloc. I do not think the extra reloc scheme is required to deal with tailcalls. Relocs should be enough. |
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 should inline this logic to where it's needed (or into an extension method). It doesn't look like we need to cache this and metadata reader + handle is available as a public API on EcmaType.
|
Cc @vitek-karas @tlakollo @dotnet/ilc-contrib - we now have a need to share code between IL Linker + NativeAOT + crossgen2. |
I've figured out that I was being too aggressive on handling overrides (I didn't account for overrides with a |
src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlValueConverter.cs
Outdated
Show resolved
Hide resolved
…vers this case, as long as we don't root P/Invokes. Remove all of the other work that interfaces with the JIT since it's unnecessary.
… cover all of the methods in CoreLib used by the runtime) any embedded linker xml root files.
… as it's difficult to determine if a method implements any interface method in the managed type system and we don't need to be perfect.
db16827 to
72a85d6
Compare
|
For the "File renamed without changes." changes, can you please check for copies under ReferenceSource and move them as well? These files are shared with illinker, the ReferenceSource is the literal copy from the linker repo - we keep them up to date by copying over the ReferenceSource files and manually applying the diffs. E.g. Please also make a copy of README.md that has the commit hash from the illinker repo. I'm kind of thinking whether we should move everything that is on the sharing plan to Common, even if it's not used by crossgen2 - so that we don't have files littered in yet another directory. Cc @dotnet/ilc-contrib @vitek-karas for thoughts. |
MichalStrehovsky
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.
Looks good to me otherwise, but I only skimmed the crossgen2-specific changes and it would be better if a crossgen dev has a look as well.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMainMethodRootProvider.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs
Outdated
Show resolved
Hide resolved
…nsky/runtime into crossgen2-no-unused-pinvoke-gen
…have it in two places
|
cc: @dotnet/crossgen-contrib can I get a second review? |
|
Could you please share data about the size reductions for the netcoreapp with the latest change? I am worried about the worst-case effects of the visibility-based rooting policy. For example, it is not unusual to see frameworks that use private reflection for binding. This will skip precompiling everything that these frameworks bind to (unless it is reachable by other means). I think we may need explicit policy to control the visibility-based rooting policy and/or enable it only in limited fashion by default. |
|
The size reduction for R2Rd NETCoreApp as a whole is pretty small: Test case is uncompressed So about 0.7MB savings for the default configuration. 151KB of that is in System.Private.CoreLib. If you feel it's important, I can make this a configurable option. |
… assembly is trimmable. It's less likely that an assembly that is trimmable will be using private reflection hackery.
|
@jkotas I added the opt-in based on |
|
It looks good enough to me. As @MichalStrehovsky said, it would be better if a crossgen dev has a look as well. |
trylek
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.
Looks great to me and nicely ties into my current experiments regarding reduction of R2R container size, thanks Jeremy!
Today, we generate bodies for all methods in crossgen2. This results in wasted space in libraries formethods that are always inlined. For System.Private.CoreLib on Windows x64 (release build), this amounts to 4653 methods totaling 298kb of size-on-disk space that will never be used.
This PR changes the rooting rules for crossgen2 as follows:
I've attached a diff from R2R dump so it's easy to see the change in CoreLib.
Original Description
Today, we generate bodies for all P/Invokes that have blittable IL stubs in crossgen2. This results in wasted space in libraries for P/Invokes that are always inlined. For System.Private.CoreLib on Windows x64 (release build), this amounts to 374 methods totaling 40kb of size-on-disk space that will never be used (we only pre-generate 10 P/Invoke stubs after this change).This PR utilizes the "recompile this method" feature to delay compilation of any P/Invoke's stubs until the P/Invoke is actually used in a way that causes it to not be inlined.
With this change, there is no change in the number of IL stubs emitted in a Hello World app on Windows x64.
Open Questions:
I've included a diff from R2R dump so it's easy to see the change this PR causes in CoreLib.
Contributes to #69758 by reducing further the cases where we pre-generate stubs in crossgen2.