An AsyncFunctionPointer, defined in Task.h, is a struct consisting of
two i32s: (1) the relative address of the async function and (2) the
size of the async context to be allocated when calling that function.
Here, such structs are emitted for every async SILFunction that is
emitted.
CfgTraits was reverted almost two weeks ago upstream but will presumably
come back. See: e025d09b216dc2239e1b502f4f277abb6fb4648a
The PPC MMA clang types were added nine days ago.
The stdlib is still crashing deep in LLVM:
```
swifterror value can only be loaded and stored from, or as a swifterror argument!
%swift.error** %2
%7 = bitcast %swift.error** %2 to %swift.opaque*
in function $ss7DecoderP16unkeyedContainers015UnkeyedDecodingC0_pyKFTj
```
From a lldb session, the function in question:
```
define protected swiftcc void @"$ss7DecoderP16unkeyedContainers015UnkeyedDecodingC0_pyKFTj"(%Ts24UnkeyedDecodingContainerP* noalias nocapture sret %0, %swift.opaque* noalias nocapture swiftself %1, %swift.error** noalias nocapture swifterror dereferenceable(8) %2, %swift.type* %3, i8** %4) #0 {
%6 = bitcast %Ts24UnkeyedDecodingContainerP* %0 to %swift.opaque*
%7 = bitcast %swift.error** %2 to %swift.opaque*
tail call swiftcc void @"$sSK5index6before5IndexQzAD_tFTj"(%swift.opaque* noalias nocapture sret %6, %swift.opaque* noalias nocapture %1, %swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %4) #0
ret void
}
```
`Builtin.createAsyncTask` takes flags, an optional parent task, and an
async/throwing function to execute, and passes it along to the
`swift_task_create_f` entry point to create a new (potentially child)
task, returning the new task and its initial context.
Implement a new builtin, `cancelAsyncTask()`, to cancel the given
asynchronous task. This lowers down to a call into the runtime
operation `swift_task_cancel()`.
Use this builtin to implement Task.Handle.cancel().
The commit with the following message wasn't previously formatted. That
oversight is fixed here.
[NFC] Construct AsyncContextLayout from module.
Previously, an IRGenFunction was being passed to the functions that
construct an AsyncContextLayout. That was not actually necessary and
prevented construction of the layout in contexts where no IRGenFunction
was present. Here that requirement is eased to requiring an IRGenModule
which is indeed required to construct an AsyncContextLayout.
Previously, an IRGenFunction was being passed to the functions that
construct an AsyncContextLayout. That was not actually necessary and
prevented construction of the layout in contexts where no IRGenFunction
was present. Here that requirement is eased to requiring an IRGenModule
which is indeed required to construct an AsyncContextLayout.
The following fields are now available when the function is a coroutine:
- TaskContinuationFunction * __ptrauth(...) yieldToCaller?
- TaskContinuationFunction * __ptrauth(...) resumeFromYield?
- TaskContinuationFunction * __ptrauth(...) abortFromYield?
- ExecutorRef calleeActorDuringYield?
- YieldTypes yieldValues...
These fields have yet to be filled in.
The following field are now available when the function is NOT a
coroutine (whereas previously they were always available):
- ResultTypes directResults...
When an async function is called from an async function, the caller
stores its own executor into the the callee's context in the
ResumeParentExecutor field.
Previously, a null task was always passed to
swift_task_alloc/swift_task_dealloc. Now that the current task is
available as one of the arguments to every async function, pass that
value along to the runtime de/allocation functions.
Because all async functions have the same signature, namely
void(%swift.task*, %swift.executor*, %swift.context*)
it is always possible to provide access to the three argument values
(the current task, current executor, and current context) within an
IRGenFunction which is async. Here, that is provided in the form of
IRGenFunction::getAsyncExecutor,IRGenFunction::getAsyncContext, and
IRGenFunction::getAsyncTask.
The previous stage of bringup only had async functions taking a single
argument: the async context. The next stage will involve the task and
executor. Here, arguments are added for those values. To begin with,
null is always passed for these values.
The majority of support comes in the form of emitting partial
application forwarders for partial applications of async functions.
Such a partial application forwarder must take an async context which
has been partially populated at the apply site. It is responsible for
populating it "the rest of the way". To do so, like sync partial
application forwarders, it takes a second argument, its context, from
which it pulls the additional arguments which were capture at
partial_apply time.
The size of the async context that is passed to the forwarder, however,
can't be known at the apply site by simply looking at the signature of
the function to be applied (not even by looking at the size associated
with the function in the special async function pointer constant which
will soon be emitted). The reason is that there are an unknown (at the
apply site) number of additional arguments which will be filled by the
partial apply forwarder (and in the case of repeated partial
applications, further filled in incrementally at each level). To enable
this, there will always be a heap object for thick async functions.
These heap objects will always store the size of the async context to be
allocated as their first element. (Note that it may be possible to
apply the same optimization that was applied for thick sync functions
where a single refcounted object could be used as the context; doing so,
however, must be made to interact properly with the async context size
stored in the heap object.)
To continue to allow promoting thin async functions to thick async
functions without incurring a thunk, at the apply site, a null-check
will be performed on the context pointer. If it is null, then the async
context size will be determined based on the signature. (When async
function pointers become pointers to a constant with a size i32 and a
relative address to the underlying function, the size will be read from
that constant.) When it is not-null, the size will be pulled from the
first field of the context (which will in that case be cast to
<{%swift.refcounted, i32}>).
To facilitate sharing code and preserving the original structure of
emitPartialApplicationForwarder (which weighed in at roughly 700 lines
prior to this change), a new small class hierarchy, descending from
PartialApplicationForwarderEmission has been added, with subclasses for
the sync and async case. The shuffling of arguments into and out of the
final explosion that was being performed in the synchronous case has
been preserved there, though the arguments are added and removed through
a number of methods on the superclass with more descriptive names. That
was necessary to enable the async class to handle these different
flavors of parameters correctly.
To get some initial test coverage, the preexisting
IRGen/partial_apply.sil and IRGen/partial_apply_forwarder.sil tests have
been duplicated into the async folder. Those tests cases within these
files which happened to have been crashing have each been extracted into
its own runnable test that both verifies that the compiler does not
crash and also that the partial application forwarder behaves correctly.
The FileChecks in these tests are extremely minimal, providing only
enough information to be sure that arguments are in fact squeezed into
an async context.
Partial applies of methods supply only self. When applies of the
resulting thick function are performed, that self was one of the
arguments is not known. As a result, self must appear after the fields
that might be supplied at the apply site, which is to say all the
arguments.
Bindings will always be supplied by the first partial apply, so they
will only be added to the async context when its full layout is known.
If they are earlier in the layout, subsequent partial applies will put
their arguments into the wrong position because they will not be privy
to the space requirements of the bindings.
For callers who do not know the actual type of the called function, e.g.
when the called function is the result of a partial apply, the offset to
the direct returns would otherwise not be known.