When an ApplyInst produces a @guaranteed result, the result's lifetime
depends on the self parameter. DestroyAddrHoisting was incorrectly hoisting
destroy_addr above uses of the @guaranteed result, destroying self while
the borrowed value was still live.
In visitApplyOperand used by GatherUniqueStorageUses, check hasGuaranteedResult()
and record all uses of the @guaranteed result as storage users.
This prevents destroy_addr from being hoisted above any use of the
@guaranteed result.
We cannot use spare bits or other overlapping storage layout tricks with fundamentally
address-only enums, and we can take advantage of this to do borrowing switches or other
in-place projections without copying the value. However, for resilient enums, the
implementation may use spare bit packing, but the type must be handled address-only
outside of its defining module, and we didn't have a way to express that with
borrowing switch. Optimization passes have also been running into problems with the
complexity that we were using `unchecked_take_enum_data_addr` sometimes as a pure
operation. This patch splits the instruction into three:
- `unchecked_inplace_enum_data_addr` represents a nondestructive in-place enum
projection. It is only allowed for enums whose projection operation is
nondestructive.
- `unchecked_take_enum_data_addr` represents a destructive enum projection,
invalidating the enum and leaving the payload to be further consumed.
This matches the current instruction's semantics.
- `unchecked_borrow_enum_data_addr` represents a borrowing enum projection.
The instruction takes a second operand for "scratch" space, which the
enum representation may be copied into in order to avoid invalidating the
enum value, so the result is dependent on the lifetime of both the
original enum and the scratch buffer. This allows for borrowing switches
over resilient enums.
`unchecked_borrow_enum_data_addr` is implemented by taking advantage of the
"address-only enums can't do spare bit optimization" property at runtime.
We inspect the operand type's bitwise-borrowability from its metadata. If
the type is bitwise-borrowable, then we are allowed to bitwise-copy the
enum to the scratch space and apply the projection to the scratch space,
preserving the original value. If the type is not bitwise-borrowable, then
we cannot use spare bit optimization in its layout, so we apply the
projection in-place.
Fixes rdar://174952822.
This function will give the wrong convention in SILGen when
using -enable-sil-opaque-values. In particular, it will say
arguments are indirect when they are not.
This is necessary because we need to model its stack-allocation
behavior, although I'm not yet doing that in this patch because
StackNesting first needs to be taught to not try to move the
deallocation.
I'm not convinced that `async let` *should* be doing a stack allocation,
but it undoubtedly *is* doing a stack allocation, and until we have an
alternative to that, we will need to model it properly.
SILGen may produce a borrow accessor result from within a local borrow scope. Such as:
```
%ld = load_borrow %self
%fwd = unchecked_ownership %ld
%ex = struct_extract %fwd, #Struct.storedProperty
end_borrow %ld
return %ex
```
This is illegal OSSA, since the return uses a value outside it's borrow scope.
Add a new SILGenCleanup transform, to turn this into valid OSSA:
```
%ld = load_borrow %self
%ex = struct_extract %ld, #Struct.storedProperty
return_borrow %ex from_scopes %ld
```
`end_borrow` instructions may end the scopes introduced by `load_borrow`
or `store_borrow` instructions, or those introduced by `begin_borrow`
whose operand's guaranteed root includes a `load_borrow`.
Previously, such scope ending instructions which end the scope
associated with a borrowing load or store were not regarded as accessing
the loaded-from or stored-to address. One consequence of this is that
they were not regarded as accessing pointers and thus not as deinit
barriers; that in turn resulted in `destroy_addr`s being hoisted into
such borrow scopes.
Here this is fixed by regarding such `end_borrow`s to access the
loaded-from or stored-to address of the scope-introducing `load_borrow`
or `store_borrow` respectively.
rdar://157772187
When hoisting destroys of aggregates, an attempt is made to fold the
destroys of individual fields into the foregoing instructions. If the
aggregate is noncopyable, this transformation is illegal.
When hoisting destroys of aggregates, an attempt is made to fold the
destroys of individual fields into the foregoing instructions. If the
aggregate is nonescapable, this transformation is illegal.
rdar://152195094
It is like `zeroInitializer`, but does not actually initialize the memory.
It only indicates to mandatory passes that the memory is going to be initialized.
If the base value of a mark_dependence is an address, that memory location must be initialized at the mark_dependence.
This requires that the mark_dependence is considered to read from the base address.
The problem with `is_escaping_closure` was that it didn't consume its operand and therefore reference count checks were unreliable.
For example, copy-propagation could break it.
As this instruction was always used together with an immediately following `destroy_value` of the closure, it makes sense to combine both into a `destroy_not_escaped_closure`.
It
1. checks the reference count and returns true if it is 1
2. consumes and destroys the operand
This corresponds to the parameter-passing convention of the Itanium C++
ABI, in which the argument is passed indirectly and possibly modified,
but not destroyed, by the callee.
@in_cxx is handled the same way as @in in callers and @in_guaranteed in
callees. OwnershipModelEliminator emits the call to destroy_addr that is
needed to destroy the argument in the caller.
rdar://122707697
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
The copy operator has been implemented and doesn't use it. Remove
`Builtin.copy` and `_copy` as much as currently possible.
Source compatibility requires that `_copy` remain in the stdlib. It is
deprecated here and just uses the copy operator.
Handling old swiftinterfaces requires that `Builtin.copy` be defined.
Redefine it here as a passthrough--SILGen machinery will produce the
necessary copy_addr.
rdar://127502242
We've been building up this exponential explosion of task-creation
builtins because it's not currently possible to overload builtins.
As long as all of the operands are scalar, though, it's pretty easy
to peephole optional injections in IRGen, which means we can at
least just use a single builtin in SIL and then break it apart in
IRGen to decide which options to set.
I also eliminated the metadata argument, which can easily be recreated
from the substitutions. I also added proper verification for the builtin,
which required (1) getting `@Sendable` right more consistently and (2)
updating a bunch of tests checking for things that are not actually
valid, like passing a function that returns an Int directly.
Previously, `visitProductLeafAccessPathNodes` required its caller to
provide both an `AccessPath` `path` and an `SILValue` `address` which
satisfied `path == AccessPath::compute(address)` to force the caller to
handle the case of an invalid `AccessPath`. Now, instead, it computes
the value itself and returns false if it's invalid.
It could be tweaked to also return false if the provided lambda returned
false but that would make the only currently extant callers less
pleasant and also would not be sufficient in the case of caller who
wanted to distinguish between an invalid `AccessPath` and a particular
leaf visit returning false.
Concurrency runtime expects discarding task operation entrypoint
function not to have result type, but the current SILGen
implementation generates reabstraction thunk to convert `() -> Void`
to `() -> T` for the operation function.
Since the `T` is always `Void` for DiscardingTG, the mismatch of result
type expectation does not cause any problem on most platforms, but the
signature mismatch causes a problem on WebAssembly.
This patch introduces new builtin operations for creating discarding
task, which always takes `() -> Void` as the operation function type.
An instruction is a deinit barrier whenever one of three component
predicates is true for it. In the case of applies, it is true whenever
one of those three predicates is true for any of the instructions in any
of its callees; that fact is cached in the side-effect analysis of every
function.
If side-effect analysis or callee analysis is unavailable, in order to
define each of those three component predicates on a
`FullApplySite`/`EndApplyInst`/`AbortApplyInst`, it would be necessary
to define them to conservatively return true: it isn't known whether any
of the instructions in any of the callees were deinit barriers.
Refactored the two versions of the deinit barrier predicate (namely
`Instruction.isDeinitBarrier(_:) and
`swift::mayBeDeinitBarrierNotConsideringSideEffects`) to handle
`FullApplySite`/`EndApplyInst`/`AbortApplyInst`s specially first (to
look up the callees' side-effect and to conservatively bail,
respectively). Asserted that the three component predicates are not
called with `FullApplySite`/`EndApplyInst`/`AbortApplyInst`s. Callers
should instead use the `isDeinitBarrier` APIs.
An alternative would be to conservatively return true from the three
components. That seems more likely to result in direct calls to these
member predicates, however, and at the moment at least there is no
reason for such calls to exist. If some other caller besides the
deinit-barrier predicates needs to call this function, side-effect
analysis should be updated to cache these three properties separately at
that point.
When an instruction `mayReadOrWriteMemory`, the `mayAccessPointer`
predicate relies on the `visitAccessedAddress` utility to determine
whether an instruction may access a pointer. That utility doesn't (and
can't, without changing the representation of the builtin) visit
`RawPointer` operands as used by memmove/memcpy builtins.
Previously, this resulted `mayAccessPointer` returning `false` for such
builtins. Here, this is fixed by making the predicate handle
`BuiltinInsts` separately. Instead of just always returning `true` in
the face of a builtin, rely on the BuiltinInfo associated with a
BuiltinInst and use `mayReadOrWriteMemory`.
rdar://120656227