Regardless of consumes of copies, if the original lexical value is not
consumed on a dead-end path, it must remain live to that dead-end.
rdar://145226757
From talking with @dgregor, it became clear that this comment was easily
interpreted as saying that AssignFresh always introduced a disconnected value...
which is not the case. Instead, AssignFresh just introduces a new value that
could have any form of isolation. The actual isolation of the value is assigned
via tryToTrackValue and eventually SILIsolationInfo::get().
CSE uses OSSA rauw which creates copies and copies that are created to optimize
across borrow scopes are unoptimizable. This PR avoids this situation for now.
executing unknown code
This means we have to claw back some performance by recognizing harmless
releases.
Such as releases on types we known don't call a deinit with unknown
side-effects.
rdar://143497196
rdar://143141695
It was used in the old redundant-load- and redundant-store-elimination passes which were replaced by new implementations.
TypeExpansionAnalysis is not used anymore.
It's sufficient just to have a struct with a kind and a value. There
aren't any cases where the payload's original type benefits from being
statically preserved--they're only ever obtained as `SILValue`s. Keep
the type safety by way of constructors.
The reason why I am doing this is that in certain cases the AST captures indices
will never actually line up with partial apply capture indices since we seem to
"smush" together closures and locally defined functions.
NOTE: The reason for the really small amount of test changes is that this change
does not change the actual output by design. The only cases I had to change were
a case where we began to emit a better diagnostic and also where I added code
coverage around _ and let _ since those require ignored_use to be implemented so
that they would be diagnosed (previously we just did not emit anything so we
couldn't emit the diagnostic at the SIL level).
rdar://142661388
Don't use previously found owned concrete values in ossa. They will consumed by
forwarding operations like init_existential_ref. Instead create an unconditional cast
of the opened existential to concrete type and use that to create a concrete apply.
Which consists of
* removing redundant `address_to_pointer`-`pointer_to_address` pairs
* optimize `index_raw_pointer` of a manually computed stride to `index_addr`
* remove or increase the alignment based on a "assumeAlignment" builtin
This is a big code cleanup but also has some functional differences for the `address_to_pointer`-`pointer_to_address` pair removal:
* It's not done if the resulting SIL would result in a (detectable) use-after-dealloc_stack memory lifetime failure.
* It's not done if `copy_value`s must be inserted or borrow-scopes must be extended to comply with ownership rules (this was the task of the OwnershipRAUWHelper).
Inserting copies is bad anyway.
Extending borrow-scopes would only be required if the original lifetime of the pointer extends a borrow scope - which shouldn't happen in save code. Therefore this is a very rare case which is not worth handling.
Canonicalize a `fix_lifetime` from an address to a `load` + `fix_lifetime`:
```
%1 = alloc_stack $T
...
fix_lifetime %1
```
->
```
%1 = alloc_stack $T
...
%2 = load %1
fix_lifetime %2
```
This transformation is done for `alloc_stack` and `store_borrow` (which always has an `alloc_stack` operand).
The benefit of this transformation is that it enables other optimizations, like mem2reg.
This peephole optimization was already done in SILCombine, but it didn't handle store_borrow.
A good opportunity to make an instruction simplification out of it.
This is part of fixing regressions when enabling OSSA modules:
rdar://140229560
This is needed after running the SSAUpdater for an existing OSSA value, because the updater can
insert unnecessary phis in the middle of the original liverange which breaks up the original
liverange into smaller ones:
```
%1 = def_of_owned_value
%2 = begin_borrow %1
...
br bb2(%1)
bb2(%3 : @owned $T): // inserted by SSAUpdater
...
end_borrow %2 // use after end-of-lifetime!
destroy_value %3
```
It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange.
* Remove dead `load_borrow` instructions (replaces the old peephole optimization in SILCombine)
* If the `load_borrow` is followed by a `copy_value`, combine both into a `load [copy]`
It hoists `destroy_value` instructions without shrinking an object's lifetime.
This is done if it can be proved that another copy of a value (either in an SSA value or in memory) keeps the referenced object(s) alive until the original position of the `destroy_value`.
```
%1 = copy_value %0
...
last_use_of %0
// other instructions
destroy_value %0 // %1 is still alive here
```
->
```
%1 = copy_value %0
...
last_use_of %0
destroy_value %0
// other instructions
```
The benefit of this optimization is that it can enable copy-propagation by moving destroys above deinit barries and access scopes.
It removes a `copy_value` where the source is a guaranteed value, if possible:
```
%1 = copy_value %0 // %0 = a guaranteed value
// uses of %1
destroy_value %1 // borrow scope of %0 is still valid here
```
->
```
// uses of %0
```
This optimization is very similar to the LoadCopyToBorrow optimization.
Therefore I merged both optimizations into a single file and renamed it to "CopyToBorrowOptimization".
In preparation for only recording the defs once, replace the
GraphNodeWorklist of defs with a SetVector. Preserve the current
visitation order by creating a worklist of indices to be visited.
Add a type which distinguishes among the types of defs that are pushed
onto the "def-use worklist". Note that it's not possible to rely on the
kind of value because the root may itself be a copy_value. For now, the
distinction is discarded as soon as the def is visited.
As the utility runs, new gens may become local: as access scopes are
determined to contain deinit barriers, their `end_access` instructions
become kills; if such an `end_access` occurs in the same block above an
initially-non-local gen, that gen is now local.
Previously, it was asserted that initially-non-local gens would not
encounter when visiting the block backwards from that gen. Iteration
would also _stop_ at the discovered kill, if any. As described above,
the assertion was incorrect.
Stopping at the discovered kill was also incorrect. It's necessary to
continue walking the block after finding such a new kill because the
book-keeping the utility does for which access scopes contain barriers.
Concretely, there are two cases:
(1) It may contain another `end_access` and above it a deinit barrier
which must result in that second scope becoming a deinit barrier.
(2) Some of its predecessors may be in the region, all the access scopes
which are open at the begin of this block must be unioned into the set
of scopes open at each predecessors' end, and more such access scopes
may be discovered above the just-visited `end_access`.
Here, both the assertion failure and the early bailout are fixed by
walking from the indicated initially-non-local gen backwards over the
entire block, regardless of whether a kill was encountered. If a kill
is encountered, it is asserted that the kill is an `end_access` to
account for the case described above.
rdar://139840307