The reason why I am doing this is that this was not part of the original
evolution proposal (it was called an extension) and after some discussion it was
realized that partial consumption would benefit from discussion on the forums.
rdar://111353459
In the next commit, I am modifying the move only checker to ensure that we
always error when partially invalidating. While doing this I discovered that
these copies were getting in the way of emitting good diagnostics in that case.
The address checker records instructions that initialize fields in its
initInsts map. Previously, that map mapped from an instruction to a
range of fields of the type. But an instruction can initialize multiple
discontiguous fields of a single value. (Indeed an attempt to add a
second range that was initialized by an already initializing
instruction--even if it were overlapping or adjacent--would have no
effect and the map wouldn't be updated.) Here, this is fixed by fixing
the representation and updating the storage whenver a new range is seen
to be initialized by the instruction. As in
https://github.com/apple/swift/pull/66728 , a SmallBitVector is the
representation chosen.
rdar://111391893
In preparation for having a third instance getting or creating the bits
affected by an instruction, introduce a typealias for maps
SILInstruction->SmallBitVector and a helper function that returns
a reference to the SmallBitVector for an instruction and two others that
set into those bits the bits from another bit vector or from a range.
The address checker records instructions that reinit fields in its
reinitInsts map. Previously, that map mapped from an instruction to a
range of fields of the type. But an instruction can use multiple
discontiguous fields of a single value. (Indeed an attempt to add a
second range that was reinit'd by an already reinit'ing
instruction--even if it were overlapping or adjacent--would have no
effect and the map wouldn't be updated.) Here, this is fixed by fixing
the representation and updating the storage whenver a new range is seen
to be reinit'd by the instruction. As in
https://github.com/apple/swift/pull/66728 , a SmallBitVector is the
representation chosen.
rdar://111356251
Reformatting everything now that we have `llvm` namespaces. I've
separated this from the main commit to help manage merge-conflicts and
for making it a bit easier to read the mega-patch.
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".
I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
During initializeLiveness, all blocks which contain destroys are to be
recorded in consuming blocks. Previously, however, certain reinits were
not being added. Here, all reinits are correctly added.
The multi-def algorithm is based off the single-def algorithm. However,
it differs from it in needing to handle "liveness holes"--uses before defs.
When identifying blocks which are originally live, the algorithm starts
from consuming blocks and walks backwards until a condition is met.
Previously, that condition was finding blocks which were originally
live. That makes sense in the single-def case: if a block is originally
live, either it was already walked through or else it was one of the
blocks discovered by liveness. In either case, its predecessors have
already been visited if appropriate.
However, in the multi-def case, this condition is too stringent. It
fails to account for the possibility of a block with has a "liveness
hole". Only stop the backwards walk if the predecessor not only (1) was
in originally live but additionally (2) "kills liveness"--doesn't have a
use before a def.
rdar://111221183
* Look through `begin_borrow` when analyzing closure values
* Treat non-escaping closures as trivial values when passed to a `partial_apply`
rdar://111046264
Like a nested partial_apply reabstraction, but without the thunk, these change
the representation of the function but can't escape it without breaking the
nonescaping closure promotion. rdar://111060678
Specifically, we were calling process on a visitor that was already moved. To
fix this I created a separate state structure that the visitor initializes and
moved the post-processing step onto the state data structure.
This seems to not be causing any problems today, but could in the future.
rdar://111060475
SILGen introduces a copy of the capture, because the semantics of escaping partial_apply's
requires the closure to take ownership of the parameters. We don't know when a closure is
strictly nonescaping or its final lifetime until ClosureLifetimeFixup runs, but that replaces
the consume of the copy with a borrow of the copy normally, hoping later passes fix it up.
We can't wait that long for move-only types, which can't be copied, so try to remove the
copy up front when the copy lives long enough and has no interfering uses other than the
partial_apply. rdar://110137169
Track liveness of self so we don't accidentally think that such types
can be memberwise reinitialized.
Fixes rdar://110232973 ([move-only] Checker should distinguish in
between field of single field struct vs parent field itself (was:
mutation of field in noncopyable struct should not trigger deinit))
The address checker records uses in its livenessUses map. Previously,
that map mapped from an instruction to a range of fields of the type.
But an instruction can use multiple discontiguous fields of a single
value. Here, such instructions are properly recorded by fixing the map
to store a bit vector for each instruction.
rdar://110676577
Previously, the checker inserted destroys after each last use. Here,
extend the lifetimes of fields as far as possible within their original
(unchecked) limits.
rdar://99681073
FieldSensitivePrunedLiveness is used as a vectorization of
PrunedLiveness. An instance of FSPL with N elements needs to be able to
represent the same states as N instances of PL.
Previously, it failed to do that in two significant ways:
(1) It attempted to save space for which elements were live by using
a range. This failed to account for instructions which are users of
non-contiguous fields of an aggregate.
apply(
@owned (struct_element_addr %s, #S.f1),
@owned (struct_element_addr %s, #S.f3)
)
(2) It used a single bit to represent whether the instruction was
consuming. This failed to account for instructions which consumed
some fields and borrowed others.
apply(
@owned (struct_element_addr %s, #S.f1),
@guaranteed (struct_element_addr %s, #S.f2)
)
The fix for (1) is to use a bit vector to represent which elements
are used by the instruction. The fix for (2) is to use a second bit
vector to represent which elements are _consumed_ by the instruction.
Adapted the move-checker to use the new representation.
rdar://110909290
In case of `var` initializations, SILGen creates a dynamic begin/end_access pair around the initialization store.
If it's an initialization (and not a re-assign) it's guanranteed that it's an exlusive access and we can convert the access to an `[init] [static]` access.
https://github.com/apple/swift/issues/66496
This closes a hole where an early return could leave some
properties from `initializes(...)` list uninitialized.
For example:
```swift
init(initialValue) initializes(_a, _b) {
_a = initialValue.0
if _a > 0 {
return
}
_b = initialValue.1
}
```
Here `_b` is not initialized when `_a > 0`.