Skip to content

Commit dea4f97

Browse files
authored
[RuntimeAsync] Merge from labs, all except JIT (#114675)
This PR includes everything from the runtimelab implementation of Runtime Async feature, except: JIT changes and tests. Some changes in between VM and JIT are hard to separate, so included here as well. The main effect of the VM changes is classification of methods by MethodReturnKind. We recognize methods that return Task/ValueTask/Task<T>/ValueTask<T> as special. A task-returning method definition will now get two methoddescs in the type system: - one has the original Task-returning signature - another is T-returning, but called with a calling convention that uses a continuation object to handle cases of incomplete result leading to suspending/resuming the chain of calls. We call the pair "variants". For most purposes the methods are identical and parallel - i.e. if generic, both have exactly same set of type parameters, if task-returning variant overrides another one in the base class, the async variant overrides the async variant in the base, etc... Both variants can signal that the result is not ready yet - via incomplete Task or by returning a Continuation object. The purpose of this duality is that async variants can be much more efficient when suspension does not happen, leading to substantial savings when a chain of awaits may be turned into a chain of calls in "async space". From the public/external point of view the async variants are an implementation detail that "does not exist", and can change in future releases. Async variants should not show up in reflection, for example. In terms of implementation logic one variant contains the user code/IL and another is a thunk that forwards to it. Which one contains the user code depends on presence of MethodImpl.Async. If there is no MethodImpl.Async the user code belongs to the task-returning variant, otherwise to the async variant. Note that for the most part Runtime Async feature is pay-for-play. JIT features will only engage if the user code is compiled with a compiler that is enlightened to use the new API for async/await. However, the creation of variant pairs for task-returning methods must happen unconditionally prior to possible use. Since these are just entry points in the type system, the impact should not be high. We will, of course measure and see if some action is needed on that. In this PR the generation of variants will require FEATURE_RUNTIME_ASYNC, which will not be defined by default, so by default none of the above will happen. Fixes #114310
1 parent fb8c7c7 commit dea4f97

File tree

116 files changed

+3735
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+3735
-279
lines changed

docs/design/coreclr/botr/clr-abi.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,34 @@ There is no defined/enforced/declared ordering between the generic parameter and
9696
call(["this" pointer] [return buffer pointer] [generics context|varargs cookie] [userargs]*)
9797
```
9898

99+
## Async
100+
101+
Async calling convention is additive to other calling conventions when supported. The set of scenarios is constrained to regular static/virtual calls and does not, for example, support PInvokes or varargs. At the minimum ordinary static calls, calls with `this` parameter or generic hidden parameters are supported.
102+
103+
Async calling convention adds an extra `Continuation` parameter and an extra return, which sematically takes precedence when not `null`. A non-null `Continuation` upon return signals that the computation is not complete and the formal result is not ready. A non-null argument means that the function is resuming and should extract the state from the `Continuation` and continue execution (while ignoring all other arguments).
104+
105+
The `Continuation` is a managed object and needs to be tracked accordingly. The GC info includes the continuation result as live at Async call sites.
106+
107+
### Returning `Continuation`
108+
To return `Continuation` we use a volatile/calee-trash register that cannot be used to return the actual result.
109+
110+
| arch | `REG_ASYNC_CONTINUATION_RET` |
111+
| ------------- | ------------- |
112+
| x86 | ecx |
113+
| x64 | rcx |
114+
| arm | r2 |
115+
| arm64 | x2 |
116+
| risc-v | a2 |
117+
118+
### Passing `Continuation` argument
119+
The `Continuation` parameter is passed at the same position as generic instantiation parameter or immediately after, if both present.
120+
121+
```
122+
call(["this" pointer] [return buffer pointer] [generics context] [continuation] [userargs]) // not x86
123+
124+
call(["this" pointer] [return buffer pointer] [userargs] [generics context] [continuation]) // x86
125+
```
126+
99127
## AMD64-only: by-value value types
100128

101129
Just like native, AMD64 has implicit-byrefs. Any structure (value type in IL parlance) that is not 1, 2, 4, or 8 bytes in size (i.e., 3, 5, 6, 7, or >= 9 bytes in size) that is declared to be passed by value, is instead passed by reference. For JIT generated code, it follows the native ABI where the passed-in reference is a pointer to a compiler generated temp local on the stack. However, there are some cases within remoting or reflection where apparently stackalloc is too hard, and so they pass in pointers within the GC heap, thus the JITed code must report these implicit byref parameters as interior pointers (BYREFs in JIT parlance), in case the callee is one of these reflection paths. Similarly, all writes must use checked write barriers.
@@ -729,4 +757,4 @@ MyStruct Test2()
729757
// We can use memset here
730758
return default;
731759
}
732-
```
760+
```

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
206206
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GenericsHelpers.cs" />
207207
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\InitHelpers.cs" />
208+
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\AsyncHelpers.CoreCLR.cs" />
208209
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
209210
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\StaticsHelpers.cs" />
210211
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\VirtualDispatchHelpers.cs" />

0 commit comments

Comments
 (0)