Skip to content

JIT: We should try to maintain full defs through async liveness #133415

Open

Description

@jakobbotsch

When we decompose stores physical promotion will often end up introducing STORE_LCL_FLDs if the destination was not fully promoted. Unfortunately these often result in async liveness thinking that the stores are partial, even though the full set is actually a full definition. This can then lead to unnecessary state being retained.

To fixes I can think of:

  • Try to make async liveness more precise and track per-field state
  • Instead of creating a set of STORE_LCL_FLD on decomposition, instead create STORE_LCL_VAR(FIELD_LIST(...)) and then expand it in lowering.

I am partial to (2), it seems simpler.

Activity

  1. added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Sep 8, 2026
  2. dotnet-policy-service commented on Sep 8, 2026

    @dotnet-policy-service
    Contributor

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

  3. jakobbotsch commented on Sep 8, 2026

    @jakobbotsch
    MemberAuthor

    I saw this problem in two places when looking at hot ASP.NET methods and asked copilot to summarize them:

    ValueTask<ReadResult> constructor temporary inside Pipe.ReadAsync

    The IR initially contained a whole zero-initialization followed by constructor field stores:

    V35 = default(ValueTask<ReadResult>)
    V35._result = readResult
    V35._obj = null
    V35._token = 0
    V35._flags = ...
    

    Physical promotion removed the whole initialization and retained only the necessary field definitions. A later whole-struct use of V35 then looked upward-exposed, so liveness carried the previous loop iteration's value.

    TransparentAwait inline argument

    The inliner initially generated a complete copy:

    V103 = V37
    

    The argument was subsequently used by:

    TransparentSuspend(V103)
    

    Physical promotion replaced the whole copy with:

    V475 = V37._obj
    STORE_LCL_FLD V103 +8
    STORE_LCL_FLD V103 +10
    STORE_LCL_FLD V103 +16
    STORE_LCL_FLD V103 +24
    STORE_LCL_FLD V103 +32
    STORE_LCL_FLD V103 +40
    STORE_LCL_FLD V103 +44
    

    The STORE_LCL_FLD nodes are partial definitions and do not contribute a full parent-local definition during non-SSA async liveness. Consequently, the later whole V103 argument use was no longer a last use, and V103 appeared live through the loop and across every suspension.

  4. removed
    untriagedNew issue has not been triaged by the area owner
    on Sep 8, 2026
  5. added this to the 12.0.0 milestone on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions