Reduces the number of _ContiguousArrayStorage metadata.
In order to support constant time bridging we do need to set the correct
metadata when we bridge to Objective-C. This is so that the type check
succeeds when bridging back from Objective-C to reuse the storage
instance rather than bridging the elements.
To support dynamically setting the `_ContiguousArrayStorage` element
type i needed to add support for optimizing `alloc_ref_dynamic`
throughout the optimizer.
Possible future improvements:
* Use different metadata such that we can disambiguate native Swift
classes during destruction -- allowing native release rather then unknown
release usage.
* Optimize the newly added semantic function
getContiguousArrayStorageType
rdar://86171143
This subclass of SILArgument should be eliminated--it's not always a
phi, and whether it is a "phi argument" has nothing whatsoever to do
with the opcode. That is a property of a value's uses, not a property of the
value.
Until then, provide a logical and useful API within the type. This
often avoids the need to explicitly cast to a SILPhiArgument type and
avoids a lot of boilerplate in code that deals with phis.
Note: PhiOperand and PhiValue are improved abstractions on top of this
API. But the SILArgument-level API is still an important bridge
between SILArgument and other phi abstractions.
Replaced ShrinkBorrowScope's own data flow with the general
BackwardReachability.
Took this opportunity to refactor and document the utility.
Taken together these changes make ShrinkBorrowScope serve as a template
for a future LexicalDestroyHoisting which will operate on owned lexical
values (rather than guaranteed as here) and hoist destroy_values (rather
than end_borrows as here) but should otherwise be quite similar.
Previously, Reachability assumed that phis were not barriers. Here,
handling for barrier phis is added. To that end, a new delegate
callback `checkReachableBarrier(PhiValue)` is added. Before marking the
beginning of a block as reached (or any of its predecessors), check
whether each argument that is a phi is a barrier. If any is, then
reachability is done.
Implemented the new method in SSADestroyHoisting by splitting apart the
classification of an instruction and the work to do in response to
visiting an instruction. Then, when visiting a PhiValue, just check
whether any of the predecessors terminators are classified as barriers.
That way, seeing that they're classified that way doesn't result in
noting down that those terminators had been reached (which indeed they
will not have been if any of the terminators from which the values are
flowing into the phi are barriers).
For trivial values, the pattern
%val = load [trivial] %addr
destroy_addr %addr
arises. Don't fold these two into
%val = load [take] %addr
because that isn't valid SIL for trivial types in OSSA.
Otherwise they will never compare the same. Also restore the test that was
updated for the previous commit to its old state. I did this to ensure that each
commit would compile successfully and so that I could show the test associated
with this commit.
* Add the possibility to bisect the individual transforms of SILCombine and SimplifyCFG.
To do so, the `-sil-opt-pass-count` option now accepts the format `<n>.<m>`, where `m` is the sub-pass number.
The sub-pass number limits the number of individual transforms in SILCombine or SimplifyCFG.
* Add an option `-sil-print-last` to print the SIL of the currently optimized function before and after the last pass, which is specified with `-sil-opt-pass-count`.
Previously, visitTransitiveEndBorrows took BorrowedValues. However,
there is at least one kind of borrow--namely,
unchecked_ownership_conversion insts--that is not currently permitted by
the BorrowedValue API. The long term fix is to make BorrowedValue
handle such instructions. For now, change visitTransitiveEndBorrows to
take SILValues so that unchecked_ownership_conversion can be passed to
the API.
rdar://87985420
Now that alloc_boxes whose lifetimes are lexical are emitted with
begin_borrow [lexical]/end_borrow, AllocBoxToStack needs to be able to
see through those new borrow scopes in order to continue stack promoting
the same boxes that it was able to before those lexical scopes were
emitted.
These macros make ApplySite.h and SILNodes.def unreadable. Getting rid
of them will save me (and I'm sure others) a lot of time whenever I
work with ApplySite.
Best practice dictates that the ApplySite abstraction be modeled in
one place. The macros serve no purpose other than obfuscation.
Introduce a new instruction `dealloc_stack_ref ` and remove the `stack` flag from `dealloc_ref`.
The `dealloc_ref [stack]` was confusing, because all it does is to mark the deallocation of the stack space for a stack promoted object.
We should be able to accept mark_uninitialized in this position. The assert was
just being careful so that the codegen that we accept here is constricted
explicitly.
rdar://86535218
Extract and rewrite the destroy hoisting algorithm originally from
CopyForwarding (in 2014).
This is now a light-weight utility for hoisting destroy_addr
instructions. Shrinking an object's memory lifetime can allow removal
of copy_addr and other optimization.
This is extremely low-overhead and can run at any optimization level
without dependency on any analysis.
This algorithm is:
- Incremental
- SSA-based
- Canonical
- Free from alias analysis
See file-level comments.
The immediate purpose is to specify and test the constraints
introduced by adding lexical variable lifetimes to SIL semantics. It
can be used as a template for end_borrow hoisting.
Ultimately, this utility can be invoked within any pass that needs to
optimize a particular uniquely identified address. It will be used to
remove much of the complexity from CopyForwarding.
So that CopyPropagation and other clients can react accordingly, pass
back a list of copy_value instructions that were rewritten by
ShrinkBorrowScope. In CopyPropagation, add each modified copy to the
copy worklist.
Fixes SR-15300: Compiler crash when using Builtin.unreachable in
initializers
Otherwise, this triggers an debug info verification assert that
invalid locations must only be on unreachable instructions.
This is why generating lifetime cleanup code should never inherit its
location from its insertion point.
During copy propagation (for which -enable-copy-propagation must still
be passed), also try to shrink borrow scopes by hoisting end_borrows
using the newly added ShrinkBorrowScope utility.
Allow end_borrow instructions to be hoisted over instructions that are
not deinit barriers for the value which is borrowed. Deinit barriers
include uses of the value, loads of memory, loads of weak references
that may be zeroed during deinit, and "synchronization points".
rdar://79149830
Rerun RLE with cutting off the base address of loads at `ref_element/tail_addr [immutable]`. This increases the chance of catching loads of immutable COW class properties or elements.
* Replace the uniqueness result of a begin_cow_mutation of an empty Array/Set/Dictionary singleton with zero.
* Remove empty begin_cow_mutation - end_cow_mutation pairs
* Remove empty end_cow_mutation - begin_cow_mutation pairs
The InstructionDeleter needs to move the callbacks during construction
to prevent the client code from using the old callbacks.
Fixes iterator invalidation bugs.
The reason why I am doing this is that we are going to be enabling lexical
lifetimes early in the pipeline so that I can use it for the move operator's
diagnostics.
To make it easy for passes to know whether or not they should support lexical
lifetimes, I included a query on SILOptions called
supportsLexicalLifetimes. This will return true if the pass (given the passed in
option) should insert the lexical lifetime flag. This ensures that passes that
run in both pipelines (e.x.: AllocBoxToStack) know whether or not to set the
lexical lifetime flag without having to locally reason about it.
This is just chopping off layers of a larger patch I am upstreaming.
NOTE: This is technically NFC since it leaves the default alone of not inserting
lexical lifetimes at all.
Thanks to the lack of critical edges in SIL, if a block B dominated by P
has a successor S which is not dominated by P, then B must have only a
single successor. Used this fact to replace a loop over successors to a
call to getSinglePredecessor.
Also added an assertion that, in the notation above, B is dominated by
P.
The key thing is that the move checker will not consider the explicit copy value
to be a copy_value that can be rewritten, ensuring that any uses of the result
of the explicit copy_value (consuming or other wise) are not checked.
Similar to the _move operator I recently introduced, this is a transparent
function so we can perform one level of specialization and thus at least be
generic over all concrete types.