Skip to content

Conversation

@jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Nov 18, 2025

In some cases the async transformation needs to introduce a new local to store the result of an async call to. If the previous store was a multi-reg call stored to an independently promoted local then we would produce illegal IR. For example, the IR

  t79 = ▌  CALL      struct <unknown method> (async) $702
        ┌──▌  t79    struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16>(P) V25 tmp9
        ▌    ref    field V25.Item3 (fldOffset=0x0) -> V275 tmp259      d:2
        ▌    int    field V25.Item2 (fldOffset=0x8) -> V276 tmp260      d:1
        ▌    ubyte  field V25.Item1 (fldOffset=0xc) -> V277 tmp261      d:1 $VN.Void

would be canonicalized into


  t79 = ▌  CALL      struct <unknown method> (async) $702
        ┌──▌  t79    struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16> V337 rat6
t5027 =    LCL_VAR   struct<System.ValueTuple`3, 16> V337 rat6
        ┌──▌  t5027  struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16>(P) V25 tmp9
        ▌    ref    field V25.Item3 (fldOffset=0x0) -> V275 tmp259      d:2
        ▌    int    field V25.Item2 (fldOffset=0x8) -> V276 tmp260      d:1
        ▌    ubyte  field V25.Item1 (fldOffset=0xc) -> V277 tmp261      d:1 $VN.Void

but that store to the promoted local with a LCL_VAR source is not legal.

Fix the problem by decomposing the store when necessary.

Fixes #119432 (comment)

In some cases the async transformation needs to introduce a new local to
store the result of an async call to. If the previous store was a
multi-reg call stored to an independently promoted local then we would
produce illegal IR. For example, the IR

```
  t79 = ▌  CALL      struct <unknown method> (async) $702
        ┌──▌  t79    struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16>(P) V25 tmp9
        ▌    ref    field V25.Item3 (fldOffset=0x0) -> V275 tmp259      d:2
        ▌    int    field V25.Item2 (fldOffset=0x8) -> V276 tmp260      d:1
        ▌    ubyte  field V25.Item1 (fldOffset=0xc) -> V277 tmp261      d:1 $VN.Void
```

would be canonicalized into

```

  t79 = ▌  CALL      struct <unknown method> (async) $702
        ┌──▌  t79    struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16> V337 rat6
t5027 =    LCL_VAR   struct<System.ValueTuple`3, 16> V337 rat6
        ┌──▌  t5027  struct
        ▌  STORE_LCL_VAR struct<System.ValueTuple`3, 16>(P) V25 tmp9
        ▌    ref    field V25.Item3 (fldOffset=0x0) -> V275 tmp259      d:2
        ▌    int    field V25.Item2 (fldOffset=0x8) -> V276 tmp260      d:1
        ▌    ubyte  field V25.Item1 (fldOffset=0xc) -> V277 tmp261      d:1 $VN.Void

```
but that store to the promoted local with a LCL_VAR source is not legal.

Fix the problem by decomposing the store when necessary.
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Nov 18, 2025
@jakobbotsch jakobbotsch added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Nov 18, 2025
@dotnet-policy-service
Copy link
Contributor

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

@jakobbotsch jakobbotsch marked this pull request as ready for review November 19, 2025 17:15
Copilot AI review requested due to automatic review settings November 19, 2025 17:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an issue in the JIT's async transformation where multi-register async calls stored to independently promoted locals would produce illegal IR. The problem occurred when ReplaceWithLclVar created an intermediate store, resulting in a LCL_VAR source for a promoted local store, which is not supported.

Key Changes

  • Added detection for multi-reg calls stored to independently promoted locals
  • Implemented decomposition logic that creates field stores using LCL_FLD nodes to access individual fields from the intermediate temporary
  • Removed the illegal promoted local store while keeping the intermediate store

@jakobbotsch
Copy link
Member Author

cc @dotnet/jit-contrib PTAL @EgorBo

@jakobbotsch jakobbotsch requested a review from EgorBo November 20, 2025 11:29
@jakobbotsch jakobbotsch merged commit 988296b into dotnet:main Nov 20, 2025
110 of 112 checks passed
@jakobbotsch jakobbotsch deleted the fix-async-missing-dner branch November 20, 2025 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants