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

[NativeAOT] don't generate __GetFieldHelper if struct is never boxed? #111544

Open
NinoFloris opened this issue Jan 17, 2025 · 4 comments
Open

[NativeAOT] don't generate __GetFieldHelper if struct is never boxed? #111544

NinoFloris opened this issue Jan 17, 2025 · 4 comments

Comments

@NinoFloris
Copy link
Contributor

NinoFloris commented Jan 17, 2025

I'm unsure how feasible this is but it would be helpful if the helper isn't emitted if the struct is never boxed (and never gets a constrained call on Equals/GetHashCode either).

I understand the difficulty of tracking whether a struct is 'boxed', as generics (and (T)(object)value casts etc) significantly complicate the analysis. Though with struct types forcing specialization it should not be impossible to get some certainty?

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 17, 2025
Copy link
Contributor

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

@MichalStrehovsky
Copy link
Member

Do you have an example where the struct is not boxed but we still generate the helper? We do not generate the helper unless the compiler thinks it could be boxed.

For example:

using System.Runtime.CompilerServices;

Consume(default, new Bar[1]);

[MethodImpl(MethodImplOptions.NoInlining)]
static void Consume(Foo f, Bar[] b) { }

struct Foo { double d; }

struct Bar { double d; }

Sizoscope reports:

Image

Notice Foo is gone.

Bar is there even though not boxed, but there are easy ways to get it as boxed. For example: call Array.GetValue. Or cast to non-generic IEnumerable and enumerate. Etc. The compiler doesn't track what exact object instance the interface methods get called on and these foundational interfaces are pretty much guaranteed to get called somewhere.

@NinoFloris
Copy link
Contributor Author

This is the repro, not great.

var action = (Struct _) => { };
struct Struct;

@MichalStrehovsky
Copy link
Member

This is the repro, not great.

This is from two things:

  1. The Invoke method on all delegates is implicitly considered reflected on. We had to do this because there's lots of code in this repo and other repos that needs to know the delegate Invoke method signature. Because of LINQ expressions we need to consider it not just target of reflection introspection, but also invocation: Generate method bodies for delegate Invoke methods #70883: that PR is the reason why this is so expensive.
  2. Types in signatures of reflected-on methods trigger logic that considers the types implicitly boxed. I did some work around that in Try rooting less stuff for reflectable method signatures #92994. Looks like I already looked if we could get rid of this for valuetypes and it is known it would be a saving if we could ("I'm already a bit uncomfortable with this change that limits it to reference types only, but it does save 0.1% on ASP.NET scenarios. We'd get triple that if we could also do this for value types, but it doesn't look likely we can."). I don't remember the details anymore, just that I looked at it and it didn't look straightforward.

Making it so that 1 or 2 doesn't apply would fix this. I don't know if it's really possible. We had reasons for both behaviors.

@MichalStrehovsky MichalStrehovsky added this to the Future milestone Jan 20, 2025
@jeffschwMSFT jeffschwMSFT removed the untriaged New issue has not been triaged by the area owner label Jan 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

3 participants