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

Extend PreInitialization Support to readonly GC fields #84431

Closed
wants to merge 3 commits into from

Conversation

Suchiman
Copy link
Contributor

@Suchiman Suchiman commented Apr 6, 2023

@MichalStrehovsky do you think the approach with the Dictionary<FieldDesc, Value> GCFields; overlaying the InstanceBytes is viable?

fixes #82993
fixes #83043

TODO:

  • InitObj must clear the GCFields
  • Switch from Keying GCFields on FieldDesc to instead using Offset to handle overlapping fields
  • figure out why on localhost, Assert.IsLazyInitialized always fails in Preinitialization tests (even if ILC claimed it did not initialize that class)

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Apr 6, 2023
@ghost
Copy link

ghost commented Apr 6, 2023

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

Issue Details

@MichalStrehovsky do you think the approach with the Dictionary<FieldDesc, Value> GCFields; overlaying the InstanceBytes is viable?

Author: Suchiman
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: -

@@ -600,7 +598,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
Value value = stack.PopIntoLocation(field.FieldType);
StackEntry instance = stack.Pop();

if (field.FieldType.IsGCPointer && value != null)
if (field.FieldType.IsGCPointer && value != null && !field.IsInitOnly)
Copy link
Member

@jkotas jkotas Apr 6, 2023

Choose a reason for hiding this comment

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

IsInitOnly on instance fields does not provide useful invariants for codegen purposes. Instance IsInitOnly fields are mutable via reflection or via unsafe code.

Copy link
Member

Choose a reason for hiding this comment

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

Can this be extended to any instance GC fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The idea behind this was to respect the following comment:

// We don't want to end up with GC pointers in the frozen region
// because write barriers can't handle that.
// We can make an exception for readonly fields.

Copy link
Member

Choose a reason for hiding this comment

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

This exception can be only made for static readonly fields that are actually immutable. Instance readonly fields are not actually immutable.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, the exception wasn't particularly sound but it felt like a compromise we could make because it helped a lot at that time. In practice reflection-setting readonly fields rarely happens outside deserialization. I originally considered extending the check to only allow this for fields that are reflection-blocked but hit layering issues that I didn't want to get into. It was basically specially crafted to allow preinit of array enumerators.

Array enumerators got more broken recently (#82993). We'll have to live with them not being preinitialized so if we don't find a workable solution for this PR, I'll submit a PR to delete the exception because it no longer serves the intended purpose and can only cause trouble.

Do you have any numbers on how many more things we can preinitialize with this? The compiler will dump them if you pass --verbose so a before/after of that list could inform how much more value this is adding.

The features in the interpreter were basically driven by what gives the best bang for the buck.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code as of this PR was able to preinit the following in a hello world repro project https://gist.github.com/Suchiman/22f9d5ebf58a820456986b93accc3af2

Copy link
Contributor Author

@Suchiman Suchiman Apr 17, 2023

Choose a reason for hiding this comment

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

Here's a comparison of before and after this change for a Hello World https://gist.github.com/Suchiman/bd346b21b0f5f7498c6780d6dddcaca7

The TL;DR; seems to be that from the 104 additional types to be preinitialized, there's 100xSZGenericArrayEnumerator and

ILC: Preinitialized type '[S.P.CoreLib]System.Collections.Generic.NonRandomizedStringEqualityComparer'
ILC: Preinitialized type '[S.P.CoreLib]System.Collections.ObjectModel.ReadOnlyCollection`1<System.Reflection.CustomAttributeData>'
ILC: Preinitialized type '[S.P.CoreLib]System.Collections.ObjectModel.ReadOnlyCollection`1<System.Reflection.CustomAttributeNamedArgument>'
ILC: Preinitialized type '[S.P.CoreLib]System.Collections.ObjectModel.ReadOnlyCollection`1<System.Reflection.CustomAttributeTypedArgument>'

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! We can basically look at this as a potential fix for #82993. The 4 extra types are a nice bonus, but maybe wouldn't be enough to motivate this change if we look at the complexity vs benefits (I think we could find less scary things to implement that will preinitialize extra 4 type; by "less scary" I mean things that don't affect the memory model of the interpreter. When I wrote on discord that I'm now afraid to touch it, I meant that I don't remember the places that may rely on invariants that would no longer hold after this change.)

I was honestly thinking of fixing #82993 by #ifdefing stuff touched in #82751 to make things look like before the change on the NativeAOT side instead of beefing up the preinitializer.

It might take a while until I can review this change - the thing is that I'd need to review the whole interpreter to ensure we don't break invariants that the rest assumes. I never expected we'd be doing stores into reference fields.

@ghost ghost closed this Jun 6, 2023
@ghost
Copy link

ghost commented Jun 6, 2023

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@ghost ghost locked as resolved and limited conversation to collaborators Jul 7, 2023
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-NativeAOT-coreclr community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NativeAOT: Type is not pre-initialized Array enumerators are not preinitialized anymore
3 participants