Skip to content

Struct DIM escape analysis driven deabstraction #126501

@NinoFloris

Description

@NinoFloris

Default interface methods on structs currently require boxing for their this parameter. At the same time the JIT often has full type information. Either it sees a concrete struct type being boxed just beforehand, or it's a constrained generic call which allows for full specialization. Even then DIM calls heap allocate, introducing unexpected *per call* costs. These calls look cheap, either as constrained calls on a generic type parameter, or as the well known box undo pattern where a struct is cast to an interface and immediately used for a call. Users reasonably expect both paths to be allocation free for known struct types.

The status quo is unfortunate because most DIMs are trivial: they return constants, throw exceptions, or delegate to other methods on the same interface. In practice, this almost never escapes, which likely holds for the vast majority of DIM implementations out there.

As far back as 2019 I opened an issue (dotnet/roslyn#35008) requesting a C#-recognized attribute to warn implementers of DIMs that should be overridden. This would help us, normal library authors, do better interface evolution. Last year I suggested that for structs implementing DIM containing interfaces, partially doing so might warrant at least a compiler notice, as this can lead to very unexpected allocations. Neither proposal has received substantive replies.

JIT optimization of DIM calls on structs could significantly reduce the cases where this particular boxing cliff shows up. A DIM can be deabstracted and synthesized as an actual interface implementation: when this does not meaningfully escape and is not used as a metadata target.

This gives us a conservative initial rule:

  • this is never boxed.
  • this never escapes except where it is used as the instance value for a call where:
    • The call target is declared on the exact interface instantiation being specialized.
    • The call target is implemented by the struct (i.e., not a DIM).

A more broadly useful, but more complex, extended rule allows valid call targets to be other DIMs, which must themselves fully conform to the stated rules.

This approach enables a DIM to call any other methods on its interface (which may themselves be DIMs) without boxing, while avoiding calls to object methods or needing to trace complex interface member resolution.

To get there the JIT could synthesize a method implementation for the struct. In practice I assume true method synthesis is hindered by the runtime <-> JIT boundary. Limiting the optimization to inlining, where the DIM would need to be a profitable candidate, isn't so bad: they generally will be good candidates (and may justify a decent bonus factor). To satisfy the extended rule profitably, any contained DIM calls *must* be inlined as well, otherwise we may end up turning one box into multiple.

For the call setup a stack copy has identical mutation semantics to a boxed struct, and since this does not escape and is never used as a metadata target, heap identity is not required.

Mechanically this gives the JIT a reasonably straightforward transformation:

  • Call setup: replace the box with a stack copy of the struct.
  • DIM body transformation:
    • Replace all ldarg.0 with ldloca.s 0 (ref to the stack copy).
    • Replace all implemented interface method calls on this (verified to target the interface instantiation only) with direct calls on the struct, passing ref this.
    • Recursively rewrite and inline any calls to other DIMs (again, verified to be conformant).

DIM escape analysis might be able to use the same infrastructure the JIT has recently built up for stack allocation of reference types. Conservative by default, falling back to boxing when it can't be certain.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions