-
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
[NativeAOT] Reflection type refactoring #93440
Conversation
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsContributes to #91704 Design:
|
I am not sure whether I like the |
Hm, it was worth a shot. Maybe RuntimeType with a null type handle would still work. My concern with going that route was around another set of bugs (assuming we can get a type handle from a RuntimeType) but maybe there's fewer of those in the end.
If reflection-free mode didn't survive this refactor I wouldn't be too sad :). |
Does it mean we'll be able to enable some of the NonGC-related opts for RuntimeType now which currently only work on CoreCLR? |
Yes, it is the goal for #91704 |
9494413
to
0e12e4a
Compare
cf63220
to
910da71
Compare
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 the extent I can reasonably review!
src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs
Outdated
Show resolved
Hide resolved
.../nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs
Outdated
Show resolved
Hide resolved
.../nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs
Outdated
Show resolved
Hide resolved
/azp run runtime-nativeaot-outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
The outerloop failures are unrelated. Fixing them in #94213. |
Contributes to dotnet#91704. When we deleted reflection blocking in dotnet#85810 we had to update `EETypeNode`/`ConstructedEETypeNode` to ensure any `MethodTable` also has metadata (constructed or not). This was needed because of how reflection was structured - we couldn't create a `RuntimeType` without knowing about the metadata. After the refactor in dotnet#93440, metadata is no longer a prerequisite to constructing a `RuntimeType`. The compiler can go back to optimizing away the metadata. This affects things like `typeof(Foo) == bar.GetType()`. No metadata is needed for this (and we do optimized the `Foo` MethodTable to unconstructed one) but we still had to generate metadata. Besides the rollback of EEType to the previous shape, this also has a bugfix for dotnet#91988 that was found later - interface types used in cast/dispatch should be considered constructed. I'm seeing 0.1 - 0.7% size saving.
With dotnet#93440 it became possible to place `RuntimeType` instances in the frozen region. This adds support for generating frozen `RuntimeType` instances within the compiler. We give these to RyuJIT whenever it asks for them. * New `FrozenObjectNode` descendant that represents a `RuntimeType` instance. We have two flavors - one wraps constructed and the other wraps necessary `MethodTable`s (we have a need for freezing both kinds to keep our ability to optimize `RuntimeTypes` used in comparisons only). * We hand out references to these whenever RyuJIT needs them. * At `MethodTable` emission time, we check whether a frozen `RuntimeType` for this `MethodTable` was generated. If so, we pre-populate `MethodTable`’s `WritableData` section with a reloc to the `RuntimeType`. Otherwise we use null as usual. * At runtime for `GetTypeFromEEType`, if `WritableData` is non-null, we just return that. Otherwise we create a pinned `RuntimeType` instance and write it into `WritableData`. In the future, we should allocate this on a frozen heap. Old codegen for `Console.WriteLine(typeof(Program))`: ```asm sub rsp,28h lea rcx,[repro_Program::`vftable' (07FF7D03154E0h)] call S_P_CoreLib_Internal_Runtime_CompilerHelpers_LdTokenHelpers__GetRuntimeType (07FF7D0253290h) mov rcx,rax call System_Console_System_Console__WriteLine_11 (07FF7D0262AC0h) nop add rsp,28h ret ``` New codegen: ```asm sub rsp,28h lea rcx,[__RuntimeType_repro_Program (07FF7A218EC50h)] call System_Console_System_Console__WriteLine_11 (07FF7A20F2680h) nop add rsp,28h ret ``` I’ll do cctor preinitialization in a subsequent PR to keep things short. Contributes to dotnet#91704.
With #93440 it became possible to place `RuntimeType` instances in the frozen region. This adds support for generating frozen `RuntimeType` instances within the compiler. We give these to RyuJIT whenever it asks for them. * New `FrozenObjectNode` descendant that represents a `RuntimeType` instance. We have two flavors - one wraps constructed and the other wraps necessary `MethodTable`s (we have a need for freezing both kinds to keep our ability to optimize `RuntimeTypes` used in comparisons only). * We hand out references to these whenever RyuJIT needs them. * At `MethodTable` emission time, we check whether a frozen `RuntimeType` for this `MethodTable` was generated. If so, we pre-populate `MethodTable`’s `WritableData` section with a reloc to the `RuntimeType`. Otherwise we use null as usual. * At runtime for `GetTypeFromEEType`, if `WritableData` is non-null, we just return that. Otherwise we create a pinned `RuntimeType` instance and write it into `WritableData`. In the future, we should allocate this on a frozen heap. Old codegen for `Console.WriteLine(typeof(Program))`: ```asm sub rsp,28h lea rcx,[repro_Program::`vftable' (07FF7D03154E0h)] call S_P_CoreLib_Internal_Runtime_CompilerHelpers_LdTokenHelpers__GetRuntimeType (07FF7D0253290h) mov rcx,rax call System_Console_System_Console__WriteLine_11 (07FF7D0262AC0h) nop add rsp,28h ret ``` New codegen: ```asm sub rsp,28h lea rcx,[__RuntimeType_repro_Program (07FF7A218EC50h)] call System_Console_System_Console__WriteLine_11 (07FF7A20F2680h) nop add rsp,28h ret ``` I’ll do cctor preinitialization in a subsequent PR to keep things short. Contributes to #91704.
…#94287) Contributes to #91704. When we deleted reflection blocking in #85810 we had to update `EETypeNode`/`ConstructedEETypeNode` to ensure any `MethodTable` also has metadata (constructed or not). This was needed because of how reflection was structured - we couldn't create a `RuntimeType` without knowing about the metadata. After the refactor in #93440, metadata is no longer a prerequisite to constructing a `RuntimeType`. The compiler can go back to optimizing away the metadata. This affects things like `typeof(Foo) == bar.GetType()`. No metadata is needed for this (and we do optimize the `Foo` MethodTable to unconstructed one) but we still had to generate metadata. Besides the rollback of EEType to the previous shape, this also has a bugfix for #91988 that was found later - interface types used in cast/dispatch should be considered constructed. I'm seeing 0.1 - 0.7% size saving.
Contributes to #91704
Design:
RuntimeType
- sealed light-weight System.Type, similar to CoreCLR RuntimeType. It has just two fieldsMethodTable*
and lazily initialized pointer to the full reflection. The light-weight method and properties are implemented usingMethodTable*
, the rest initializes the full reflectionRuntimeTypeInfo
lazily and calls it to do the work.RuntimeTypeInfo
- internal, lazily created full reflection. It is similar to theCache
attached to RuntimeType in CoreCLR. The reflection-free mode is implemented by blocking creation ofRuntimeTypeInfo
and throwing instead.Some reflection micro-benchmark may regress with this change on native AOT. It is expected due to the extra indirections, but it makes the reflection more pay-for-play and a bit closer to how it is implemented in CoreCLR.