[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
MichalStrehovsky
left a comment
There was a problem hiding this comment.
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 reflectionRuntimeTypeInfolazily and calls it to do the work.RuntimeTypeInfo- internal, lazily created full reflection. It is similar to theCacheattached to RuntimeType in CoreCLR. The reflection-free mode is implemented by blocking creation ofRuntimeTypeInfoand 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.