Start treating the null {Can}GenericSignature as a regular signature
with no requirements and no parameters. This not only makes for a much
safer abstraction, but allows us to simplify a lot of the clients of
GenericSignature that would previously have to check for null before
using the abstraction.
Currently the debug info infrastructure inside SIL can only associate a
source variable with a single (simple) SSA value. Which is insufficient
to preserve (correct) debug info across SIL-level optimizations -- for
example, SROA that decompose or even eliminate aggregate-type obejcts.
By adding DIExpression into SIL, we are able to reconstruct the
connection between a source variable and its SSA value counterpart, even
across optimizations. This patch adds such support into in-memory
representation for SIL instructions and the SILParser/Printer. The
following patch will add changes for the IRGen part.
Don't allow an owned call argument to be considered a valid BorrowingOperand.
More generally, make sure there is a perfect equivalence between valid
BorrowingOperand and the corresponding OperandOwnership kind.
The MemoryLifetimeVerifier issued a false alarm if a tuple with a trivial enum-case (e.g. an Optional nil) is stored to a memory location.
rdar://79781939
A place to define invariants on OperandOwnership that passes can rely
on for convenience.
Starting with a simple invariant the OperandOwnership::Borrow is a
valid BorrowingOperand.
Just a case where the verifier was too sensitive. The only places that we use
this in terms of optimization would just not optimize in this case, so no
miscompiles are possible. That being said, we should ensure that SILGen doesn't
hit a verifier error after it emits code.
rdar://78698170 [SR-14680]
The verifier just invokes this method, so we aren't losing any verification in
the SILVerifier itself.
The reason why I am extracting this information into a helper is that often
times one hits these structural assertions in the verifier making one have to
track down where in a pass the bad location was actually inserted. To make these
easier to find, I am going to change the SILBuilder to invoke these structural
comparisons so that we can catch these problems at the call site making it
easier to fix code.
- Introduce an UnownedSerialExecutor type into the concurrency library.
- Create a SerialExecutor protocol which allows an executor type to
change how it executes jobs.
- Add an unownedExecutor requirement to the Actor protocol.
- Change the ABI for ExecutorRef so that it stores a SerialExecutor
witness table pointer in the implementation field. This effectively
makes ExecutorRef an `unowned(unsafe) SerialExecutor`, except that
default actors are represented without a witness table pointer (just
a bit-pattern).
- Synthesize the unownedExecutor method for default actors (i.e. actors
that don't provide an unownedExecutor property).
- Make synthesized unownedExecutor properties `final`, and give them
a semantics attribute specifying that they're for default actors.
- Split `Builtin.buildSerialExecutorRef` into a few more precise
builtins. We're not using the main-actor one yet, though.
Pitch thread:
https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425
In theory we could map opened archetypes per module because opened archetypes _should_ be unique across the module.
But currently in some rare cases SILGen re-uses the same opened archetype in multiple functions.
The fix is to add the SILFunction to the map's key.
That also requires that we update the map whenever instructions are moved from one function to another.
This fixes a compiler crash.
rdar://76916931
Instead, put the archetype->instrution map into SIlModule.
SILOpenedArchetypesTracker tried to maintain and reconstruct the mapping locally, e.g. during a use of SILBuilder.
Having a "global" map in SILModule makes the whole logic _much_ simpler.
I'm wondering why we didn't do this in the first place.
This requires that opened archetypes must be unique in a module - which makes sense. This was the case anyway, except for keypath accessors (which I fixed in the previous commit) and in some sil test files.
Through various means, it is possible for a synchronous actor-isolated
function to escape to another concurrency domain and be called from
outside the actor. The problem existed previously, but has become far
easier to trigger now that `@escaping` closures and local functions
can be actor-isolated.
Introduce runtime detection of such data races, where a synchronous
actor-isolated function ends up being called from the wrong executor.
Do this by emitting an executor check in actor-isolated synchronous
functions, where we query the executor in thread-local storage and
ensure that it is what we expect. If it isn't, the runtime complains.
The runtime's complaints can be controlled with the environment
variable `SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL`:
0 - disable checking
1 - warn when a data race is detected
2 - error and abort when a data race is detected
At an implementation level, this introduces a new concurrency runtime
entry point `_checkExpectedExecutor` that checks the given executor
(on which the function should always have been called) against the
executor on which is called (which is in thread-local storage). There
is a special carve-out here for `@MainActor` code, where we check
against the OS's notion of "main thread" as well, so that `@MainActor`
code can be called via (e.g.) the Dispatch library's
`DispatchQueue.main.async`.
The new SIL instruction `extract_executor` performs the lowering of an
actor down to its executor, which is implicit in the `hop_to_executor`
instruction. Extend the LowerHopToExecutor pass to perform said
lowering.
The comment in LowerHopToActor explains the design here.
We want SILGen to emit hops to actors, ignoring executors,
because it's easier to fully optimize in a world where deriving
an executor is a non-trivial operation. But we also want something
prior to IRGen to lower the executor derivation because there are
useful static optimizations we can do, such as doing the derivation
exactly once on a dominance path and strength-reducing the derivation
(e.g. exploiting static knowledge that an actor is a default actor).
There are probably phase-ordering problems with doing this so late,
but hopefully they're restricted to situations like actors that
share an executor. We'll want to optimize that eventually, but
in the meantime, this unblocks the executor work.
Remove the "initialization loop" for LastSeenScope in verifyDebugScopeHoles.
It's not required because LastSeenScope is initialized the same way in the main loop.
And most importantly: this loop was missing the check for cleanup locations.
rdar://75374671
And rename MemoryDataflow -> BitDataflow.
MemoryLifetime contained MemoryLocations, MemoryDataflow and the MemoryLifetimeVerifier.
Three independent things, for which it makes sense to have them in three separated files.
NFC.
OwnershipEliminator lowers destroy_value [poison] to debug_value
[poison].
IRGen overwrites all references in shadow copies with a sentinel value
in place of debug_value [poison].
Part 2/2: rdar://75012368 (-Onone compiler support for early object
deinitialization with sentinel dead references)
Also, relax the check for enums a bit. Instead of only accepting single-payload enums, just require that the payload type is the same for an enum location.
Refactor SILGen's ApplyOptions into an OptionSet, add a
DoesNotAwait flag to go with DoesNotThrow, and sink it
all down into SILInstruction.h.
Then, replace the isNonThrowing() flag in ApplyInst and
BeginApplyInst with getApplyOptions(), and plumb it
through to TryApplyInst as well.
Set the flag when SILGen emits a sync call to a reasync
function.
When set, this disables the SIL verifier check against
calling async functions from sync functions.
Finally, this allows us to add end-to-end tests for
rdar://problem/71098795.
Otherwise the verifier will fail on trivial cases like this:
sil hidden [ossa] @testAssign : $@convention(thin) <T> (@inout T, @in T) -> () {
bb0(%0 : $*T, %1 : @owned $T):
store %1 to [assign] %0 : $*T
%3 = tuple ()
return %3 : $()
}
SIL memory lifetime failure in @testAssign: memory is initialized at
function return but shouldn't memory location:
%1 = argument of bb0 : $T
Verify that
* the destination address is an alloc_stack
* the stack location is not modified beside a store_borrow
* the stack location has been initialized when used
cloning
forwardingOwnershipKind can differ from the operand's ownershipKind. We
need to copy forwardingOwnershipKind while cloning these instructions.
Also print the forwarding ownership kind when it differs from its
operand's ownershipKind
This is a follow up of #36063
This is kind of complicated, because an enum can be trivial for one case and not trivial for another case. We need to check at which parts of the function we can prove that the enum does (or could) have a trivial case. In such a branch, it's not required in SIL to destroy the enum location.
Also, document the rules and requirements for enum memory locations in SIL.rst.
rdar://73770085
This is a follow-up to e9d557ae28. The
debug_value for the guard let binding is introduced by SGF.emitStmtCondition(),
so we also need to enter the debug scope before running that function.
rdar://74538257