With PE/COFF, one cannot reference a data symbol directly across the
binary module boundary. Instead, the reference must be indirected
through the Import Address Table (IAT) to allow for position
independence.
When generating a reference to a AsyncFunctionPointer ({i8*, i32}), we
tag the pointer as being indirected by tagging bit 1 (with the
assumption that native alignment will ensure 4/8 byte alignment, freeing
the bottom 2 bits at least for bit-packing). We tweak the
v-table/witness table emission such that all references to the
AsyncFunctionPointer are replaced with the linker synthetic import
symbol with the bit packing:
~~~
.quad __imp_$s1L1CC1yyYaKFTu+1
~~~
rather than
~~~
.quad $s1L1CC1yyYaKFTu
~~~
Upon access of the async function pointer reference, we open-code the
check for the following:
~~~
pointer = (pointer & 1) ? *(void **)(pointer & ~1) : pointer;
~~~
Thanks to @DougGregor for the discussion and the suggestion for the
pointer tagging. Thanks to @aschwaighofer for pointers to the code that
I had missed. Also, thanks to @SeanROlszewski for the original code
sample that led to the reduced test case.
Fixes: SR-15399
Define the possible runtime effects of an instruction in an enum `RuntimeEffect`.
Add a new utility `swift:getRuntimeEffect` to estimate the runtime effects of an instruction.
Also, add a mechanism to validate the correctness of the analysis in IRGen: annotate all runtime functions in RuntimeFunctions.def with the actual effect what the runtime function has or can have. Then check if the effects of emitted runtime functions for an instruction match what `getRuntimeEffect` predicts.
This check is only enabled on demand by defining the CHECK_RUNTIME_EFFECT_ANALYSIS macro in RuntimeEffect.h
@objc actors implicitly inherit from the new, hidden
`SwiftNativeNSObject` class that inherits from `NSObject` yet provides
Swift-native reference counting, which is important for the actor
runtime's handling of zombies. However, `SwiftNativeNSObject` is only
available in the Swift runtime in newer OS versions (e.g., macOS
12.0/iOS 15.0), and is available in the back-deployed _Concurrency
library, but there is no stable place to link against for
back-deployed code. Tricky, tricky.
When back-deploying @objc actors, record `NSObject` as the superclass
in the metadata in the binary, because we cannot reference
`SwiftNativeNSObject`. Then, emit a static initializer to
dynamically look up `SwiftNativeNSObject` by name (which will find it
in either the back-deployment library, on older systems, or in the
runtime for newer systems), then swizzle that in as the superclass of
the @objc actor.
Fixes rdar://83919973.
A new LLVM IR affordance that allows expressing conditions under which globals
can be removed/dropped (even when marked with @llvm.used) is being discussed at:
- <https://reviews.llvm.org/D104496>
- <https://lists.llvm.org/pipermail/llvm-dev/2021-September/152656.html>
This is a preliminary implementation that marks runtime lookup records (namely
protocol records, type descriptors records and protocol conformance records)
with the !llvm.used.conditional descriptors. That allows link-time / LTO-time
removal of these records (by GlobalDCE) based on whether they're actually used
within the linkage unit. Effectively, this allows libraries that have a limited
and known set of clients, to be optimized against the client at LTO time, and
significantly reduce the code size of that library.
Parts of the implementation:
- New -conditional-runtime-records frontend flag to enable using !llvm.used.conditional
- IRGen code that emits these records can now emit these either as a single contiguous
array (asContiguousArray = true, the old way), which is used for JIT mode, or
as indivial globals (asContiguousArray = false), which is necessary for the
!llvm.used.conditional stripping to work.
- When records are emitted as individual globals, they have new names of
"\01l_protocol_" + mangled name of the protocol descriptor, and similarly for
other records.
- Fixed existing tests to account for individual records instead of a single array
- Added an IR level test, and an end-to-end execution test to demonstrate that
the !llvm.used.conditional-based stripping actually works.
When we deploy to a minimum target that is not known to support extended
frame information the function prolog of Swift async functions will
contain a reference to swift_async_extendedFramePointerFlags. This
reference needs to be weak like any async symbols.
rdar://83412550
- Virtual calls are done via a @llvm.type.checked.load instrinsic call with a type identifier
- Type identifier of a vfunc is the base method's mangling
- Type descriptors and class metadata get !type markers that list offsets and type identifiers of all vfuncs
- The -enable-llvm-vfe frontend flag enables VFE
- Two added tests verify the behavior on IR and by executing a program
Tracking this as a single bit is actually largely uninteresting
to the runtime. To handle priority escalation properly, we really
need to track this at a finer grain of detail: recording that the
task is running on a specific thread, enqueued on a specific actor,
or so on. But starting by tracking a single bit is important for
two reasons:
- First, it's more realistic about the performance overheads of
tasks: we're going to be doing this tracking eventually, and
the cost of that tracking will be dominated by the atomic
access, so doing that access now sets the baseline about right.
- Second, it ensures that we've actually got runtime involvement
in all the right places to do this tracking.
A propos of the latter: there was no runtime involvement with
awaiting a continuation, which is a point at which the task
potentially transitions from running to suspended. We must do
the tracking as part of this transition, rather than recognizing
in the run-loops that a task is still active and treating it as
having suspended, because the latter point potentially races with
the resumption of the task. To do this, I've had to introduce
a runtime function, swift_continuation_await, to do this awaiting
rather than inlining the atomic operation on the continuation.
As part of doing this work, I've also fixed a bug where we failed
to load-acquire in swift_task_escalate before walking the task
status records to invoke escalation actions.
I've also fixed several places where the handling of task statuses
may have accidentally allowed the task to revert to uncancelled.
Same issue as with TBDGen. Clang doesn't hold onto the Datalayout object
anymore, but instead keeps the description string that is constructed on
the fly. This patch updates IRGen to behave the same way.
Rather than using group task options constructed from the Swift parts
of the _Concurrency library and passed through `createAsyncTask`'s
options, introduce a separate builtin that always takes a group. Move
the responsibility for creating the options structure into IRGen, so
we don't need to expose the TaskGroupTaskOptionRecord type in Swift.
introduce new options parameter to all task spawning
[Concurrency] ABI for asynclet start to accept options
[Concurrency] fix unittest usages of changed task creation ABI
[Concurrency] introduce constants for parameter indexes in ownership
[Concurrency] fix test/SILOptimizer/closure_lifetime_fixup_concurrency.swift
Changes the task, taskGroup, asyncLet wait funtion call ABIs.
To reduce code size pass the context parameters and resumption function
as arguments to the wait function.
This means that the suspend point does not need to store parent context
and resumption to the suspend point's context.
```
void swift_task_future_wait_throwing(
OpaqueValue * result,
SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
AsyncTask *task,
ThrowingTaskFutureWaitContinuationFunction *resume,
AsyncContext *callContext);
```
The runtime passes the caller context to the resume entry point saving
the load of the parent context in the resumption function.
This patch adds a `Metadata *` field to `GroupImpl`. The await entry
pointer no longer pass the metadata pointer and there is a path through
the runtime where the task future is no longer available.
The immediate desire is to minimize the set of ABI dependencies
on the layout of an ExecutorRef. In addition to that, however,
I wanted to generally reduce the code size impact of an unsafe
continuation since it now requires accessing thread-local state,
and I wanted resumption to not have to create unnecessary type
metadata for the value type just to do the initialization.
Therefore, I've introduced a swift_continuation_init function
which handles the default initialization of a continuation
and returns a reference to the current task. I've also moved
the initialization of the normal continuation result into the
caller (out of the runtime), and I've moved the resumption-side
cmpxchg into the runtime (and prior to the task being enqueued).
The current code generation will emit an autibsp after adjusting the
stack pointe for the tail call. If callee and caller argument area does
not match this would fail.
The `coro.end.async` intrinsic allow specifying a function that is to be
tail-called as the last thing before returning.
LLVM lowering will inline the `must-tail-call` function argument to
`coro.end.async`. This `must-tail-call` function can contain a
`musttail` call.
```
define @my_must_tail_call_func(void (*)(i64) %fnptr, i64 %args) {
musttail call void %fnptr(i64 %args)
ret void
}
define @async_func() {
...
coro.end.async(..., @my_must_tail_call_func, %return_continuation, i64 %args)
unreachable
}
```
First, just call an async -> T function instead of forcing the caller
to piece together which case we're in and perform its own copy. This
ensures that the task is actually kept alive properly.
Second, now that we no longer implicitly depend on the waiting tasks
being run synchronously, go ahead and schedule them to run on the
global executor.
This solves some problems which were blocking the work on TLS-ifying
the task/executor state.
This is conditional on UseAsyncLowering and in the future should also be
conditional on `clangTargetInfo.isSwiftAsyncCCSupported()` once that
support is merged.
Update tests to work either with swiftcc or swifttailcc.
This patch adds support for custom C++ destructors. The most notable thing here, I think, is that this is the first place a struct type has a custom destructor. I suspect with more code we will expose a few places where optimization passes need to be fixed to account for this.
One of many patches to fix SR-12797.
This adds new kinds of link entities corresponding to the three
dispatch thunk link entity kinds:
- DispatchThunkAsyncFunctionPointer
- DispatchThunkInitializerAsyncFunctionPointer
- DispatchThunkAllocatorAsyncFunctionPointer
of adding a property.
This better matches what the actual implementation expects,
and it avoids some possibilities of weird mismatches. However,
it also requires special-case initialization, destruction, and
dynamic-layout support, none of which I've added yet.
In order to get NSObject default actor subclasses to use Swift
refcounting (and thus avoid the need for the default actor runtime
to generally use ObjC refcounting), I've had to introduce a
SwiftNativeNSObject which we substitute as the superclass when
inheriting directly from NSObject. This is something we could
do in all NSObject subclasses; for now, I'm just doing it in
actors, although it's all actors and not just default actors.
We are not yet taking advantage of our special knowledge of this
class anywhere except the reference-counting code.
I went around in circles exploring a number of alternatives for
doing this; at one point I basically had a completely parallel
"ForImplementation" superclass query. That proved to be a lot
of added complexity and created more problems than it solved.
We also don't *really* get any benefit from this subclassing
because there still wouldn't be a consistent superclass for all
actors. So instead it's very ad-hoc.