Skip to content

Conversation

@VSadov
Copy link
Member

@VSadov VSadov commented Sep 18, 2025

A few simple/trivial fixes for issues found in #119432

@VSadov VSadov added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI area-VM-coreclr runtime-async labels Sep 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.

@dotnet-policy-service
Copy link
Contributor

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

@VSadov VSadov marked this pull request as ready for review September 19, 2025 00:43
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 addresses several issues found when enabling runtime async functionality in the .NET Libraries. The changes fix problems related to GS cookie checks, ValueTask async thunks, and async context handling.

  • Fixes GS cookie check register conflicts with continuation returns by using a different register
  • Adds proper ValueTask support to async thunks including struct-specific handling for method invocation
  • Ensures split blocks from SaveAsyncContexts are properly visited for additional awaits

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/coreclr/vm/method.hpp Adds declaration for IsValueTaskAsyncThunk method
src/coreclr/vm/corelib.h Defines ValueTask and ValueTaskAwaiter method bindings for async support
src/coreclr/vm/asyncthunks.cpp Implements ValueTask detection and specialized handling in async thunks
src/coreclr/jit/codegenxarch.cpp Fixes GS cookie check register selection to avoid conflicts
src/coreclr/jit/async.cpp Ensures proper block traversal after context save splits and improves assertions

@VSadov
Copy link
Member Author

VSadov commented Sep 19, 2025

There is a failure on x86 and seems related to the change.
The scratch registers on x86 are only RBM_EAX|RBM_ECX|RBM_EDX, and all can be used to return something. In a worst case EAX is LNGARG_LO EDX is LNGARG_HI and ECX is CONTINUATION_RET.

So, if we also need a GS check in the epilog, we are in trouble.

@jakobbotsch
Copy link
Member

jakobbotsch commented Sep 19, 2025

There is a failure on x86 and seems related to the change. The scratch registers on x86 are only RBM_EAX|RBM_ECX|RBM_EDX, and all can be used to return something. In a worst case EAX is LNGRET_LO EDX is LNGRET_HI and ECX is CONTINUATION_RET.

So, if we also need a GS check in the epilog, we are in trouble.

I think we can detect and use a callee save in this case. Just need to take care to specially save it. Similar to this logic:

// Parameter homing may need an additional register to handle conflicts if
// all callee trash registers are used by parameters.
regMaskTP homingCandidates = genGetParameterHomingTempRegisterCandidates();
if (((homingCandidates & ~intRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLINT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLINT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for integer parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}
if (((homingCandidates & ~floatRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLFLOAT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLFLOAT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for float parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}

@VSadov
Copy link
Member Author

VSadov commented Sep 19, 2025

There is a failure on x86 and seems related to the change. The scratch registers on x86 are only RBM_EAX|RBM_ECX|RBM_EDX, and all can be used to return something. In a worst case EAX is LNGARG_LO EDX is LNGARG_HI and ECX is CONTINUATION_RET.
So, if we also need a GS check in the epilog, we are in trouble.

I think we can detect and use a callee save in this case. Just need to take care to specially save it. Similar to this logic:

// Parameter homing may need an additional register to handle conflicts if
// all callee trash registers are used by parameters.
regMaskTP homingCandidates = genGetParameterHomingTempRegisterCandidates();
if (((homingCandidates & ~intRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLINT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLINT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for integer parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}
if (((homingCandidates & ~floatRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLFLOAT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLFLOAT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for float parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}

My plan is to use REG_ECX on x86 for GS check, and in async methods save/restore around the check. I think that should work.

@jakobbotsch
Copy link
Member

There is a failure on x86 and seems related to the change. The scratch registers on x86 are only RBM_EAX|RBM_ECX|RBM_EDX, and all can be used to return something. In a worst case EAX is LNGARG_LO EDX is LNGARG_HI and ECX is CONTINUATION_RET.
So, if we also need a GS check in the epilog, we are in trouble.

I think we can detect and use a callee save in this case. Just need to take care to specially save it. Similar to this logic:

// Parameter homing may need an additional register to handle conflicts if
// all callee trash registers are used by parameters.
regMaskTP homingCandidates = genGetParameterHomingTempRegisterCandidates();
if (((homingCandidates & ~intRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLINT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLINT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for integer parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}
if (((homingCandidates & ~floatRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLFLOAT) == RBM_NONE)
{
regMaskTP extraRegMask = RBM_ALLFLOAT & ~homingCandidates & ~regSet.rsMaskResvd;
assert(extraRegMask != RBM_NONE);
regNumber extraReg = genFirstRegNumFromMask(extraRegMask);
JITDUMP("No temporary registers are available for float parameter homing. Adding %s\n", getRegName(extraReg));
regSet.rsSetRegsModified(genRegMask(extraReg));
}

My plan is to use REG_ECX on x86 for GS check, and in async methods save/restore around the check. I think that should work.

That might work too, assuming we can push registers at that point (don't recall if the cookie check is in the formal epilog or not).

@VSadov
Copy link
Member Author

VSadov commented Sep 19, 2025

I do not think GS check is in epilog, in no-gc sense.

So another plan: x86 uses the same code as before this PR, but if we have both async method and long return, use REG_ARG_1 (EDX) and push/pop it around the check.

@VSadov
Copy link
Member Author

VSadov commented Sep 21, 2025

/azp run runtime-coreclr gcstress-extra, runtime-coreclr gcstress0x3-gcstress0xc

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@VSadov
Copy link
Member Author

VSadov commented Sep 22, 2025

All tests pass in normal mode with async enabled, but under GC stress there are a few failures, which all also happen in a parallel no-op PR, thus are not regressions.

@VSadov VSadov merged commit 87ecdeb into dotnet:main Sep 22, 2025
112 checks passed
@VSadov VSadov deleted the gsArg branch September 22, 2025 15:25
@VSadov
Copy link
Member Author

VSadov commented Sep 22, 2025

/backport to release/10.0

@github-actions
Copy link
Contributor

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

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

Projects

None yet

2 participants