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

Unify RuntimeType with the shared RuntimeType #91704

Closed
3 tasks
MichalStrehovsky opened this issue Sep 6, 2023 · 7 comments
Closed
3 tasks

Unify RuntimeType with the shared RuntimeType #91704

MichalStrehovsky opened this issue Sep 6, 2023 · 7 comments

Comments

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented Sep 6, 2023

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:

  • Roll back Emit less metadata for not-reflection-visible types #91660. Instead of doing the extra analysis, we should no longer need to generate any metadata for unconstructed MethodTables (basically roll back EETypeNode/ConstructedEETypeNode/etc. to how it looked before Get rid of reflection blocked types #85810 that triggered the bugfix)
  • Preinitialization of System.RuntimeType instances and related RyuJIT features that can directly reference System.RuntimeType addresses.
  • Allocate lazily created System.RuntimeType instances on frozen heap
@ghost
Copy link

ghost commented Sep 6, 2023

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

Issue Details

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:

Author: MichalStrehovsky
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: 9.0.0

@MichalPetryka
Copy link
Contributor

MichalPetryka commented Sep 7, 2023

Wouldn't this require NativeAOT to get rid of the RuntimeType subclasses for this?

@MichalStrehovsky
Copy link
Member Author

Wouldn't this require NativeAOT to get rid of the RuntimeType subclasses for this?

Yes, that's the intention.

jkotas added a commit that referenced this issue Oct 31, 2023
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
@jkotas
Copy link
Member

jkotas commented Oct 31, 2023

Preinitialization of System.RuntimeType instances and related RyuJIT features that can directly reference System.RuntimeType addresses.

@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.

@EgorBo
Copy link
Member

EgorBo commented Nov 1, 2023

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

MichalStrehovsky added a commit to MichalStrehovsky/runtime that referenced this issue Nov 2, 2023
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.
@MichalStrehovsky
Copy link
Member Author

Preinitialization of System.RuntimeType instances and related RyuJIT features that can directly reference System.RuntimeType addresses.

@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.

Looking into it!

Thanks for the help offer Egor!

MichalStrehovsky added a commit to MichalStrehovsky/runtime that referenced this issue Nov 3, 2023
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.
MichalStrehovsky added a commit that referenced this issue Nov 6, 2023
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.
MichalStrehovsky added a commit to MichalStrehovsky/runtime that referenced this issue Nov 6, 2023
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.
MichalStrehovsky added a commit that referenced this issue Nov 6, 2023
…#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.
MichalStrehovsky added a commit that referenced this issue Nov 13, 2023
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.
@MichalStrehovsky
Copy link
Member Author

This has been resolved.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants