-
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
Unify RuntimeType with the shared RuntimeType #91704
Comments
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsOptimistically placing this in 9.0 milestone. We should unify the reflection stack with the stack shared with Mono/CoreCLR-JIT. Things we can enable once that's done:
|
Wouldn't this require NativeAOT to get rid of the RuntimeType subclasses for this? |
Yes, that's the intention. |
Design - `RuntimeType` - sealed light-weight System.Type, similar to CoreCLR RuntimeType. It has just two fields `MethodTable*` and lazily initialized pointer to the full reflection. The light-weight method and properties are implemented using `MethodTable*`, the rest initializes the full reflection `RuntimeTypeInfo` lazily and calls it to do the work. - `RuntimeTypeInfo` - internal, lazily created full reflection. It is similar to the `Cache` attached to RuntimeType in CoreCLR. The reflection-free mode is implemented by blocking creation of `RuntimeTypeInfo` 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. Contributes to #91704
@MichalStrehovsky Feel free to pick this up and enable this optimization in ilc. It should be pretty straightforward now that RuntimeType is prepared for it. I do not think that I am going to have time to do it anytime soon. |
I can help with the JIT side once RuntimeType become frozen, ideally it's a matter of implementing some, currently no-op, JIT-VM apis and might need some JIT changes as well for relocable constants |
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.
Looking into it! Thanks for the help offer Egor! |
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.
Unblock serializing `RuntimeType` instances from the static constructor interpreter. With this we can now preinitialize `static readonly Type s_foo = typeof(Foo)` and RyuJIT will optimize reads of `s_foo` into loading a constant value. Contributes to dotnet#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.
Unblock serializing `RuntimeType` instances from the static constructor interpreter. With this we can now preinitialize `static readonly Type s_foo = typeof(Foo)` and RyuJIT will optimize reads of `s_foo` into loading a constant value. Contributes to #91704.
This has been resolved. |
Optimistically placing this in 9.0 milestone.
We should unify the reflection stack with the stack shared with Mono/CoreCLR-JIT.
Things we can enable once that's done:
The text was updated successfully, but these errors were encountered: