- Notifications
You must be signed in to change notification settings - Fork5.2k
[RuntimeAsync] Enable runtime async in Libraries partition#119432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Conversation
Tagging subscribers to this area:@mangod9 |
VSadov commentedSep 6, 2025
VSadov commentedSep 6, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
A lot of tests actually run and pass when I run this locally. More than I expected. :-) |
VSadov commentedSep 6, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Some JIT asserts: |
VSadov commentedSep 6, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Something odd is going on with sync context in a few cases: |
VSadov commentedSep 9, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I have a fix for the following assert: looking at others. |
src/coreclr/jit/async.cpp Outdated
| // TODO: (async) we do not need to save ByRef-containing locals | ||
| // as by the spec an await turns them into zero-inited state. | ||
| // For now just store/restore as if there are no gc refs. | ||
| // This is mostly to handle the "fake" live-across-await byrefs | ||
| // in min-opts, since C#-compiled code by itself does not let | ||
| // byrefs be live across awaits. | ||
| unsigned objCount = layout->HasGCByRef() ?0 : layout->GetGCPtrCount(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We will need to figure out what is creating these LIR edges that are live across the await and stop doing that. This fix will replace assert with bad codegen instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I typically see the assert when compilingDebug code for Task-returning methods with ref-like parameters.
Ex:
publicstaticTaskWhenAll(paramsReadOnlySpan<Task>tasks){
async
This method is not async, but it's thunk will be async and will also have a byref-like parameter.
The part that the edge lives across the await is likely a result ofDebug not tracking liveness precisely.
Once we have zeroing of byrefs that live across await, we may not need this.
This is not a fix. It is a workaround. - my goal is to get all Libraries tests pass or find something truly blocking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Assuming that it is a result ofDebug emit, perhaps forcing thunks to always compile optimized might be an alternative workaround, if there is an easy way to force.
I just thought of this workaround first and it seems working well enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The part that the edge lives across the await is likely a result of Debug not tracking liveness precisely.
There are two sources of live state across async calls in JIT IR:
- Locals. These behave like IL locals and can be multiply defined and multiply used. They are what liveness analysis treat. They are added by
AsyncLiveness::GetLiveLocals. - LIR edges. These are single-def single-use (SDSU) values that are defined in one place in a basic block and consumed later in the same basic block. When they overlap an async call (defined before, used after) they must also be preserved on heap. Liveness analysis doesnot treat these; they are known to be live when they overlap the call. They are added by
AsyncTransformation::LiftLIREdges.
The liveness imprecision only comes into play for (1). However, we already ignore byref locals for these. It happens here:
runtime/src/coreclr/jit/async.cpp
Lines 507 to 515 inb0b30e7
| if ((dsc->TypeIs(TYP_BYREF) && !dsc->IsImplicitByRef()) || | |
| (dsc->TypeIs(TYP_STRUCT) && dsc->GetLayout()->HasGCByRef())) | |
| { | |
| // Even if these are address exposed we expect them to be dead at | |
| // suspension points. TODO: It would be good to somehow verify these | |
| // aren't obviously live, if the JIT creates live ranges that span a | |
| // suspension point then this makes it quite hard to diagnose that. | |
| returnfalse; | |
| } |
That just leaves (2). But since these are known to be live, it is (almost?) always going to be a bug that resulted in these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Good point! I will log an issue on this - to follow up with real fix.
VSadov commentedSep 11, 2025
I see only 26 failed tests now in Chk/Ret configuration on win-x64. There are 23 cases of There are also 8 cases of Also fixing one thing sometimes exposes another, but so far with every fix/workaround I see more tests passing. |
VSadov commentedSep 17, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
The AVs in Logged an issue on that -#119796 and pushed a possible fix here. Now my local run is down to:
|
jakobbotsch commentedNov 18, 2025
Fix for this is in#121749. |
VSadov commentedNov 18, 2025
Ah, I missed that part, I thought we restore only on logical returns. How did it work before? Was it not the same try/finally save/restore, but around call sites? |
jakobbotsch commentedNov 18, 2025
Previously we only restored in the finally on a synchronous return. If we suspended we did not change the Thread contexts, but we saved the contexts from before the call in the continuation, and on resumption we then restored those. (That gives the same behavior for async to async calls, but using the contexts from before the call is not right if the callee modifies them and is not itself async, in which case the caller should see and save the new contexts.) |
VSadov commentedNov 18, 2025
If I read this correctly, now the callee restores when it returns continuation, whereas before the caller would not restore if it gets continuation from callee. In such case an alternative "fix" could be to not restore in the callee when continuation is returned. Is that correct? |
jakobbotsch commentedNov 18, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Not exactly. It would be easy for us to skip the restore in the callee -- the JIT has no issue returning from suspension without invoking the finally. But that wouldn't be correct, since the caller will read the contexts from If the callee is async it needs to save and restore the contexts, even on suspension, because the caller will read the them from the |
VSadov commentedNov 18, 2025
I see. The caller must read from the Thread, because the caller does not know if callee is async or something Task-returning (in which case context changes prior to suspending must be observed), but that requires that an async callee must restore even when suspending. I missed this detail of the change. |
jakobbotsch commentedNov 18, 2025
The correspondence with async1 code is:
The two last items above happen after the callee was already called. Hence if the callee changed the contexts then the caller observes those changed contexts. |
VSadov commentedNov 18, 2025
I've pushed the currently pending fixes to this PR. |
368314a toc8fbaffCompaream11 commentedNov 20, 2025
I think it wasn’t the intention of#121749 to close this PR. Reopening. |
67e08a2 tod183716Compare
Uh oh!
There was an error while loading.Please reload this page.
Mostly to see what happens and how far we can get with this.