-
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
Expand ILLink/ILC support for 'eliminate dead branches around typeof comparisons' to work on internal types too #110300
Comments
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
Tagging subscribers to this area: @dotnet/illink |
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
Tagging subscribers to this area: @dotnet/illink |
Since anyone can have their own System.Type descendants that may return anything as their ToString or FullName, we'd need guarantees that this is the real deal (RuntimeType). So it would have to be something like
We'd probably not want to introduce patterns that only perform well when rewritten. I wonder if it would be feasible to introduce an UnsafeAccessor that can do the equivalent for Type.GetType at compile time (e.g. |
These sort of global lookup tables are very common in interop solution. It came up in the context of Android interop recently. Android interop has one of these tables as well (with different set of problems). We may want to look at creating an API that makes it possible to build these tables in the most efficient way possible for each form factor. |
Related (archived repository): |
I was thinking a bit about how to approach this. The existing switch statement over strings is pretty difficult to analyze (the C# compiler needs to rewrite this significantly since there's no switch over strings in IL). Having a cascade of ifs doesn't perform very well. We probably don't want a cascade of ifs. In a sense, this is similar to #12260, but the necessity to be able to access internal types makes it more challenging. But still, it would be nice if the potential solution was applicable to both. I don't know if there is one. The shape that would work the best would be something that is easy to analyze and easy to eliminate. Off the top of my head, I see two mechanisms: method calls, or custom attributes. Method calls would need to be to methods that don't take parameters (if a method takes parameters, it's much harder to erase it). The method call approach would be something like this: x.Register<Foo, Bar>();
x.Register<Baz, Bazz>();
// ... We'd mark the There would be another API to completement the The other approach would be similar, but instead of method calls, we would encode these pairs into custom attributes. Trimming would also treat them specially and if the first type is unused, the whole attribute would be dropped. The disadvantage of attributes is that they are observable - so dropping custom attributes produces observable side effects (and behavior difference between trimmed and untrimmed apps). We could argue that those are "our" attributes and only our blessed APIs should access them, but anyone can. This could be used to implement a "was this type trimmed?" API. We don't really want such API. Now for accessing internal types - the custom attribute approach would be simpler, we can just have custom attributes accept strings instead of System.Type (or strings in addition to System.Type). The method call approach has a problem because we might not be able to access the type used as the generic parameter. We'd need a new kind of UnsafeAccessor that can call instantiated generic methods using inaccessible type parameters. It's not impossible either. |
Overview
This is a follow up to #102248 which primarily affects CsWinRT. In order to support marshalling types we don't own (meaning we cannot attach a vtable to them via an attribute), the AOT generators in CsWinRT also generate a global lookup table with all necessary vtables for types it has seen as possibly used across the ABI boundary, in a given project. This looks something like this:
This works just fine, but we noticed the linker isn't able to remove all branches for types that are not constructed.
Repro steps
dotnet publish -r win-x64 .\ConsoleApp13.csproj
Here's what we see in sizoscope:
That is, sizoscope isn't able to trim branches for types not constructed when checked via the type name.
Note
We cannot do
GetType() == typeof()
checks, because that would not work with internal types (which we also need to handle).Proposal
We need a generalized way for this to work in such a way that we can also leverage this for internal types. For instance, either:
GetType() == Type.GetType("The.Type.Name")
To clarify, the issue is internal types from other assemblies, ie. such that we can't just do
typeof(TheType)
in code.cc. @MichalStrehovsky
The text was updated successfully, but these errors were encountered: