[clr-interp] Fix handling of continuation return when it is not used by the method#128403
Merged
Merged
Conversation
…by the method
Consider the scenario where we are emitting an async method call where the return value is not used at all by the method and, by the time we are actually doing the suspend operation, a new live variable is created. This means that the new variable could share the same offset as the return offset. Example interp IR:
```
IR_0074: call [64 <- 96], .Program:ThrowsAsync[System.__Canon](System.Func`1[System.Threading.Tasks.Task])
// var at offset 56 was present on the interp execution stack and it gets moved to a temporary that shares the same offset with the dead return
// this means that, when we restore the context and write the continuation return to the interp stack, we overwritte this
IR_0078: mov.8 [64 <- 56],
IR_007b: handle.continuation [72 <- nil], AsyncSuspendData[continuationTypeHnd=0x7fff7980c310, flags=3144, liveLocalsIntervals=[{64, 71},], zeroedLocalsIntervals=[], keepAliveOffset=32], 0x7fff797479c0 (CORINFO_HELP_ALLOC_CONTINUATION)(indirect)
IR_007f: capture.context.on.suspend [80 <- 72], AsyncSuspendData[continuationTypeHnd=0x7fff7980c310, flags=3144, liveLocalsIntervals=[{64, 71},], zeroedLocalsIntervals=[], keepAliveOffset=32]
IR_0083: restore.contexts.on.suspend [72 <- 72 0 16], AsyncSuspendData[continuationTypeHnd=0x7fff7980c310, flags=3144, liveLocalsIntervals=[{64, 71},], zeroedLocalsIntervals=[], keepAliveOffset=32]
IR_0089: handle.continuation.suspend [nil <- 72], AsyncSuspendData[continuationTypeHnd=0x7fff7980c310, flags=3144, liveLocalsIntervals=[{64, 71},], zeroedLocalsIntervals=[], keepAliveOffset=32]
IR_008c: handle.continuation.resume [nil <- nil], AsyncSuspendData[continuationTypeHnd=0x7fff7980c310, flags=3144, liveLocalsIntervals=[{64, 71},], zeroedLocalsIntervals=[], keepAliveOffset=32]
IR_008e: ldind.i8 [96 <- 64], 8
```
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @BrzVlad, @janvorli, @kg |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adjusts the CoreCLR interpreter async suspend/resume machinery so that, when an awaited call’s return value is effectively unused (and its stack slot may be immediately reused for a different live temp), the resume path does not copy the continuation return value back into the interpreter stack and accidentally overwrite a live value.
Changes:
- In the interpreter compiler, detect the “unused return value temp” case and mark
returnValueVarStackOffsetas disabled (-1) instead of patching it to a stack offset. - In the interpreter execution engine, gate the resume-time return-value copy on
returnValueVarStackOffset != -1(rather thanreturnValueContinuationDataSize > 0).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/coreclr/vm/interpexec.cpp | Changes resume logic to only copy the continuation result into the interpreter stack when a valid return-value stack offset is present. |
| src/coreclr/interpreter/compiler.cpp | Updates async suspend metadata fix-up so unused return-value temps don’t get a stack offset patched in, preventing overwrite on resume. |
This was referenced May 20, 2026
Open
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
BrzVlad
added a commit
that referenced
this pull request
May 25, 2026
The return value of a call is normally a local var, but it can later be promoted to a global var if it is left on the IL stack and then later used in a different basic block. We don't need any special handling if the return value of an async call is a global var. Assertion incorrectly added in #128403
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consider the scenario where we are emitting an async method call where the return value is not used at all by the method and, by the time we are actually doing the suspend operation, a new live variable is created. This means that the new variable could share the same offset as the return offset. Example interp IR:
This bug was exposed by #127931, which changed the order of the write to the result var, during resume.