Skip to content

[API Proposal]: Introduce an intrinsic for more efficient lambda generation #85014

@MichalPetryka

Description

@MichalPetryka

Background and motivation

Roslyn currently caches lambda delegates in static mutable fields in nested classes and initializes them lazily at the use site (guarded by a null check).
This pattern is inefficient for current runtimes, being hard to detect and optimize correctly, leading to dead code being left in generated assembly and making optimizations like delegate inlining more tricky.
Discussion here ended up with two possible solutions: creating a runtime intrinsic that'd create and cache the delegate for Roslyn or caching the delegates in static readonly fields which CoreCLR and Native AOT can analyze easily even today but that'd come with some metadata cost.

The API can create delegates closed over null for most cases, putting them on the Frozen Object Heap.
For delegates to instance methods on generic types, the runtime would be required to create an instance instead. Such support would make the implementation relatively more complicated, as such it's not decided yet whether the API should work then or throw. If it is to work, the API should forbid the owning type from having a cctor and a finalizer due to only creating instances in some cases and leaving them uninitialized.
The API can't always return the same instance due to not being able to always expand as intrinsic for shared generics, the instance count will however always be small and they'll always be value equal to each other.

In AOT scenarios, the API requires to match a specific IL pattern recognized by the runtime to work for non reflection visible methods.

cc @jkotas @EgorBo @jaredpar @CyrusNajmabadi

API Proposal

From #125901:

public class RuntimeHelpers
{
    [Intrinsic]
    public static TDelegate GetDelegate<TDelegate>(nint method) where TDelegate : Delegate;
}

API Usage

This would be used internally by Roslyn when emitting lambdas.

Alternative Designs

Have Roslyn store delegates in static readonly fields - would work with CoreCLR and Native AOT, not require any runtime changes, but it'd introduce either more metadata bloat or more eager delegate object allocation (we'd either need a separate nested class per lambda or calling one lambda would then create delegates for all the lambdas in the nested class).

Risks

All runtimes including Mono would need to have this implemented.
It's difficult to guarantee the same instance will be always returned on all runtimes.


Tasks:

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Runtime.CompilerServiceshelp wanted[up-for-grabs] Good issue for external contributorsin-prThere is an active PR which will close this issue when it is merged

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions