Commit Graph

35 Commits

Author SHA1 Message Date
Nate Chandler
4dec48f9a6 [SSADestroyHoisting] Don't fold over trivial use.
Given an aggregate addr `%agg` with trivial subobject addr `%triv` and
nontrivial subobject addr `%sub` which `%triv` is projected from,
```
  Aggregate   <- %agg
    Subobject <- %sub
      Trivial <- %triv
      ...
    ...
```

after `%sub` is destroyed, `%triv` is no longer initialized.  As a
result, it's not valid to fold a destroy_addr of %agg into a sequence of
`load [copy]`s and `copy_addr`s if there's an access to `%triv` after
the `load [copy]`/`copy_addr` of `%sub` (or some intermediate
subobject).

In other words, transforming
```
  copy_addr %sub
  load [trivial] %triv
  destroy_addr %agg
```
into
```
  copy_addr [take] %sub
  load [trivial] %triv
```
is invalid.

During destroy_addr folding, prevent that from happening by keeping
track of the trivial fields that have already been visited.  If a
trivial field is seen more than once, then bail on folding.  This is
the same as what is done for non-trivial fields except that there's no
requirement that all trivial fields be destroyed.
2023-01-05 15:18:36 -08:00
Nate Chandler
ed623d7b64 [NFC] Shortened SIL [init] flag.
Instead of writing out [initalization] for some instructions, use [init]
everywhere.
2022-10-27 10:38:54 -07:00
Nate Chandler
a2a621a3aa [SSADestroyHoisting] Barriers use callee analysis.
Pass a BasicCalleeAnalysis instance to isDeinitBarrier.  This will
enable SSADestroyHoisting to hoist destroy_addrs over applies of
functions that are not themselves deinit barriers.
2022-10-18 21:23:22 -07:00
Nate Chandler
7ea336367d [NFC] Port isDeinitBarrier to Swift.
Added new C++-to-Swift callback for isDeinitBarrier.

And pass it CalleeAnalysis so it can depend on function effects.  For
now, the argument is ignored.  And, all callers just pass nullptr.

Promoted to API the mayAccessPointer component predicate of
isDeinitBarrier which needs to remain in C++.  That predicate will also
depends on function effects.  For that reason, it too is now passed a
BasicCalleeAnalysis and is moved into SILOptimizer.

Also, added more conservative versions of isDeinitBarrier and
maySynchronize which will never consider side-effects.
2022-10-18 21:23:22 -07:00
Josh Soref
730b16c569 Spelling siloptimizer
* access
* accessed
* accesses
* accessor
* acquiring
* across
* activated
* additive
* address
* addresses'
* aggregated
* analysis
* and
* appropriately
* archetype
* argument
* associated
* availability
* barriers
* because
* been
* beginning
* belongs
* beneficial
* blocks
* borrow
* builtin
* cannot
* canonical
* canonicalize
* clazz
* cleanup
* coalesceable
* coalesced
* comparisons
* completely
* component
* computed
* concrete
* conjunction
* conservatively
* constituent
* construct
* consuming
* containing
* covered
* creates
* critical
* dataflow
* declaration
* defined
* defining
* definition
* deinitialization
* deliberately
* dependencies
* dependent
* deserialized
* destroy
* deterministic
* deterministically
* devirtualizes
* diagnostic
* diagnostics
* differentiation
* disable
* discipline
* dominate
* dominates
* don't
* element
* eliminate
* eliminating
* elimination
* embedded
* encounter
* epilogue
* epsilon
* escape
* escaping
* essential
* evaluating
* evaluation
* evaluator
* executing
* existential
* existentials
* explicit
* expression
* extended
* extension
* extract
* for
* from
* function
* generic
* guarantee
* guaranteed
* happened
* heuristic
* however
* identifiable
* immediately
* implementation
* improper
* include
* infinite
* initialize
* initialized
* initializer
* inside
* instruction
* interference
* interferes
* interleaved
* internal
* intersection
* intractable
* intrinsic
* invalidates
* irreducible
* irrelevant
* language
* lifetime
* literal
* looks
* materialize
* meaning
* mergeable
* might
* mimics
* modification
* modifies
* multiple
* mutating
* necessarily
* necessary
* needsmultiplecopies
* nonetheless
* nothing
* occurred
* occurs
* optimization
* optimizing
* original
* outside
* overflow
* overlapping
* overridden
* owned
* ownership
* parallel
* parameter
* paths
* patterns
* pipeline
* plottable
* possible
* potentially
* practically
* preamble
* precede
* preceding
* predecessor
* preferable
* preparation
* probably
* projection
* properties
* property
* protocol
* reabstraction
* reachable
* recognized
* recursive
* recursively
* redundant
* reentrancy
* referenced
* registry
* reinitialization
* reload
* represent
* requires
* response
* responsible
* retrieving
* returned
* returning
* returns
* rewriting
* rewritten
* sample
* scenarios
* scope
* should
* sideeffects
* similar
* simplify
* simplifycfg
* somewhat
* spaghetti
* specialization
* specializations
* specialized
* specially
* statistically
* substitute
* substitution
* succeeds
* successful
* successfully
* successor
* superfluous
* surprisingly
* suspension
* swift
* targeted
* that
* that our
* the
* therefore
* this
* those
* threshold
* through
* transform
* transformation
* truncated
* ultimate
* unchecked
* uninitialized
* unlikely
* unmanaged
* unoptimized key
* updataflow
* usefulness
* utilities
* villain
* whenever
* writes

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2022-10-03 18:31:33 -04:00
Nate Chandler
8d55e09bf3 [SSADestroyHoisting] Lifetimes alter arg hoisting.
Arguments whose lifetimes are not lexical should be hoisted without
respect to deinit barriers.  On the other hand, inout arguments
explicitly annotated @_lexical should respect deinit barriers.
2022-08-22 15:27:59 -07:00
Nate Chandler
80ba5b588b [SSADestroyHoisting] Only lexical alloc_stacks respect deinit barriers. 2022-08-10 19:27:57 -07:00
Nate Chandler
8775148f7b [SILOpt] Used SetVector for fast contains check.
IterableBackwardReachability just requires an iterable list of gens.
ShrinkBorrowScope, LexicalDestroyHoisting, and SSADestroyHoisting all
need to be able to check whether a given instruction is scope ending
quickly.  Use a SmallSetVector rather than a SmallVector for the gens in
all three.
2022-05-24 14:15:27 -07:00
Nate Chandler
24979d48e0 [SSADestroyHoisting] Adopt new utilities.
Instead of doing one or two non-iterative BackwardReachability runs,
do a single run of IterativeBackwardReachability.  During that, pause
after discovery/local dataflow and use VisitBarrierAccessScopes to
determine which end_access instructions in the discovered region are
barriers.  Add those instructions as kills to the dataflow.  Finally run
the global dataflow.

Enables SSADestroyHoisting to hoist destroys over loops.

Addresses a correctness issue where access scopes which were open at
barrier blocks were not promoted to barriers, resulting in destroy_addrs
getting hoisted into unrelated access scopes.
2022-05-21 12:56:51 -07:00
Nate Chandler
672ec76d54 [NFC] Removed spurious namespacing. 2022-03-16 20:45:40 -07:00
Nate Chandler
87d0c9fd6e [SSADestroyHoisting] Bitcasts obstruct folding.
Make loads and copy_addrs of casts of the underlying storage barriers to
folding.  Destroying the target address may not be equivalent to
destroying the source address: for example, if the target address is a
generic and the source address is AnyObject, specialization may turn the
generic into a trivial type; the destruction of that trivial type fails
to destroy the original stored AnyObject, resulting in a leak.
2022-03-16 11:33:51 -07:00
Nate Chandler
266668d010 [SSADestroyHoisting] Fold into sequences.
Previously, destroy_addrs were folded into copy_addrs and load [copy]s
to produce copy_addr [take]s and load [take]s respectively, but only if
the source of the load/copy was exactly the address being destroyed.

Generalize that to a single-block sequence of copy_addrs and load
[copy]s of projections of the address being destroyed.
2022-03-16 11:33:51 -07:00
Nate Chandler
febdf30e5b [SSADestroyHoisting] Tie barriers to lifetimes.
Only respect deinit barriers when lexical lifetimes are enabled.  If
they aren't, hoist destroy_addrs of all addresses aggressively
regardless of whether doing so involves hoisting over deinit barriers.
2022-03-16 11:33:51 -07:00
Nate Chandler
4ffde96a31 [SSADestroyHoisting] Fixed pointer handling.
Previously, SSADestroyHoisting was attempting to check whether an
unknown use of a variable was an address_to_pointer.
UniqueStorageUseVisitor, however, doesn't call back with that
instruction.  Instead, it adds its uses to the stack of uses to visit.

Instead, we need to check whether the use was produced by an
address_to_pointer or more generally whether it's a
BuiltinRawPointerType.
2022-03-09 10:08:16 -08:00
Nate Chandler
2de9171749 [NFC] Used convenience method. 2022-02-23 12:37:24 -08:00
Nate Chandler
e0262ebc0e [SSADestroyHoisting] Fixed barrier check.
Previously, FindBarrierAccessScopes::checkReachablePhiBarrier was not
looking at the terminator of predecessors but rather looking at the
terminator of block itself.  Previously, in cases where the current
block's terminator was in fact a barrier, that resulted in failing to
hoist any live-in access scopes.

Now that we aren't running the data flow twice, the result was worse: in
cases where the current block's terminator was a barrier but there was
no access scope in play, no barrier would be added at all.
2022-02-23 12:35:49 -08:00
Nate Chandler
26853f4be6 [SSADestroyHoisting] Avoid second per-var dataflow.
In order to determine which end_access instructions are barriers to
hoisting, a data flow which looks for access scopes containing barriers
is run.  Those scopes that do contain barriers are added to a set.  When
the second pass runs, the end_access instructions corresponding to
scopes in that set (i.e. the ends of scopes which contain barriers) are
treated as barriers.

In the common case where there are no barrier access scopes, though,
running two dataflows per variable is wasteful.  Avoid that by just
checking whether we found any scopes that are barriers.  If we didn't,
then we already visited all the barrier instructions and were told by
BackwardReachability which blocks had reachable ends and begins.

Tweaked the first data flow to record the barriers and the blocks in
DeinitBarriers.  In DeinitBarriers::compute, if no access scopes that
are barriers were found, stop working.  If any were found, clear what
had been recorded so far and run the second data flow.

In order to be able to clear everything, switched from using
BasicBlockSet and BasicBlockSetVector to SmallPtrSet<SILBasicBlock *>
and SmallPtrSetVector<SILBasicBlock *>.
2022-02-22 16:25:10 -08:00
Nate Chandler
40e57e185a [SSADestroyHoisting] Split destroy from copy.
As was done with store [init], transform instructions like

    copy_addr %n to %m

into the sequence

    destroy_addr %m
    copy_addr %n to [initialization] %m

in order to create more opportunities for hoisting destroys.

After hoisting, if these opportunities for hoisting don't result in
hoisting actually occurring, recombine the two instructions.
2022-02-21 09:21:26 -08:00
Nate Chandler
cea8d75604 [NFC] Replaced block with continue. 2022-02-21 09:21:26 -08:00
Nate Chandler
39078cf343 [SSADestroyHoisting] Aliasable addr args respect deinit barriers.
Previously, all arguments using the inout convention were hoisted
ignoring deinit barriers.  That was incorrect because @inout_aliasable
addresses are modifications but aren't exclusive.  Here, that's fixed by
only allowing arguments with the @inout convention to be hoisted.
2022-02-18 18:57:22 -08:00
Nate Chandler
0d8866c358 [SSADestroyHoisting] Don't fold into unrelated scopes.
If a load [copy] appears near the end of the scope protecting access to
another address and a destroy_addr of the loaded address appears
afterwards, don't fold the destroy into the scope.  The reason is that
doing so could allow a deinit which previously executed outside the
exclusivity scope to subsequently execute within it.
2022-02-18 18:45:15 -08:00
Nate Chandler
be1ebe0fd5 [SSADestroyHoisting] Recombine store [init]s.
Before hoisting destroy_addrs, we split store [assign]s into
destroy_addrs and store [init]s.  If those destroy_addrs were not able
to be hoisted, though, recombine the two back into a store [assign].
Doing so avoids introducing extra ARC traffic.
2022-02-18 12:52:26 -08:00
Nate Chandler
271bfdcc07 [SSADestroyHoisting] Note barrier access scopes.
Added a second backward reachability data flow that determines whether
any open access scopes contain barriers.  The end_access instructions
for those access scopes are themselves barriers.
2022-02-18 10:10:18 -08:00
Nate Chandler
767e5096eb [SSADestroyHoisting] Fold into access scopes.
If the destroy_addr's barrier is an end_access, try to fold with copies
or loads that occur inside the scope so long as there are no barriers
between the destroy_addr and the instruction it is to be fold with.
2022-02-18 10:10:18 -08:00
Nate Chandler
4ad25e91a3 [SSADestroyHoisting] Extracted classifying insts.
Extract code for classifying instructions out of the one data flow where
it is currently used into the DeinitBarriers type.  This will facilitate
a second data flow which needs to access the same info and adding an
isBarrier member function to DeinitBarriers for use by folding.
2022-02-18 10:10:18 -08:00
Nate Chandler
304d2bba5a [SSADestroyHoisting] Modifying ignores deinit barriers.
Restructured its DeinitBarrier's type a bit to avoid having to pass
KnownStorageUses and ignoreDeinitBarriers around too much.
2022-02-18 10:10:18 -08:00
Nate Chandler
092b6f2b54 [SSADestroyHoisting] Expand store [assign]s first.
To create more opportunities for hoisting.
2022-02-18 10:10:18 -08:00
Nate Chandler
bd8626c8a9 [SSADestroyHoisting] Hoist everything inner first.
In addition to hoisting destroy_addrs for alloc_stacks and function
arguments, also hoist begin_access [modify] insts.  Hoist starting from
innermost scopes and proceeding outwards.
2022-02-18 10:10:17 -08:00
Nate Chandler
d227cef874 [MemAccessUtils] Visit access as unique storage uses.
Add a new member functino UniqueStorageUseVisitor::visitBeginAccess and
call it when GatherUniqueStorageUses runs.
2022-02-18 10:10:17 -08:00
Nate Chandler
ffea048bab [SSADestroyHoisting] Removed incorrect mutation.
The classifyInstruciton function shouldn't modify state.
2022-02-18 10:10:17 -08:00
Nate Chandler
94ef0af9ea [SILOpt] Reachability handles phis.
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).
2022-02-14 17:12:47 -08:00
Nate Chandler
53c728efb6 [SSADestroyHoisting] Don't fold trivial loads.
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.
2022-02-10 20:18:17 -08:00
Nate Chandler
553f49d090 [Gardening] Fixed typo. 2022-02-10 20:18:04 -08:00
Nate Chandler
137a1607eb [SSADestroyHoisting] Deleted copy/assign ctors.
For the DeinitBarriers helper class.
2022-02-10 20:17:48 -08:00
Andrew Trick
c8a2130554 Add a SSADestroyHoisting utility and pass
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.
2021-12-22 11:32:57 -08:00