This is a simple "utility" pass that canonicalizes SSA SILValues with
respect to copies and destroys. It is a self-contained, provably
complete pass that eliminates spurious copy_value instructions from
scalar SSA SILValues. It fundamentally depends on ownership SIL, but
otherwise can be run efficiently after any other pass. It separates
the pure problem of handling scalar SSA values from the more important
and complex problems:
- Promoting variables to SSA form (PredictableMemOps and Mem2Reg
partially do this).
- Optimizing copies within "SIL borrow" scopes (another mandatory pass
will be introduced to do this).
- Composing and decomposing aggregates (SROA handles some of this).
- Coalescing phis (A BlockArgumentOptimizer will be introduced as part
of AddressLowering).
- Removing unnecessary retain/release when nothing within its scope
may release the same object (ARC Code Motion does some of this).
Note that removing SSA copies was more obviously necessary before the
migration to +0 argument convention.
Replacing an alloc_stack of a struct/tuple with multiple alloc_stacks of the struct/tuple elements should only be done if the elements are somehow accessed individually.
If not, e.g. if the whole struct/tuple is just copied, there is no benefit of doing SROA.
Although this change has little impact by its own (some small code size wins), it is important for the improvement of let-property optimization.
Once the algorithm has begun hoisting destroys globally, there's no
way to cleanly bailout. The previous attempt to bailout could result
in an assert or lost destroy in release mode.
This is continued fall-out from changes in the previous release to
upstream SILGen or mandatory passes, such as PredictableMemOps, that
no longer preserve natural variable lifetimes.
In this case, we end up with SIL like this before CopyForwarding:
bb(%arg)
%local_addr = alloc_stack
store %arg to %local_addr
%payload = switch_enum(%arg)
retain %arg
store %arg to %some_addr
destroy_addr %local_addr
release_value %arg
We're attempting to hoist the destroy_addr to its last use, but can't
because the lifetimes of the alloc_stack (%local_addr) and the value
being stored on the stack (%arg) have become mixed up by an upstream
pass. We actually detect this situation now in order to bail-out of
destroy hoisting. Sadly, the bailout might only partially recover in
the case of interesting control flow, as happens in the test case's
Graph.init function. This triggers an assert, but in release mode it
simply drops the destroy.
Fixed <rdar://problem/43888666> [SR-8526]: Memory leak after switch in
release configuration.
SIL passes were violating the existing invariant on non-cond-br
critical edges in several places. I fixed the places that I could
find. Wherever there was a post-pass to "clean up" critical edges, I
replaced it with a a call to verification that the critical edges
aren't broken in the first place.
We still need to eliminate critical edges entirely before enabling
ownership SIL.
The client of this interface naturally expects to get back the
incoming phi value. Ignoring dominance and SIL ownership, the incoming
phi value and the block argument should be substitutable.
This method was actually returning the incoming operand for
checked_cast and switch_enum terminators, which is deeply misleading
and has been the source of bugs.
If the client wants to peek though casts, and enums, it should do so
explicitly. getSingleTerminatorOperand[s]() will do just that.
When compiling benchmark/single-source/DictTest.swift, a multiplication can overflow in SILPerformanceInliner::isProfitableToInline(). Notice when the value is large enough to overflow and zero out the value it would have been subtracted from.
Partially revert a recent attempt to fix build warnings.
Fixes: <rdar://problem/43824067> Swift CI:
1. OSS - Swift (Tools Opt+No Assert, Stdlib Opt+DebInfo, Test Simulator)
- OS X (master) - SimplifyCFG crashes at AssertCommon.swift:283:10
visitSetForConflicts Had a bug wherein we might change the accessSet while iterating over it by recording a conflict. This makes our current iterator invalid.
Work-around this issue by restarting the iteration in case we made any changes to the accessSet
Similarly, we do that for anywhere where in we recorded a conflict
In order to make this reasonable, I needed to shift responsibilities
around a little; the devirtualization operation is now responsible for
replacing uses of the original apply. I wanted to remove the
phase-separation completely, but there was optimization-remark code
relying on the old apply site not having been deleted yet.
The begin_apply aspects of this aren't testable independently of
replacing materializeForSet because coroutines are currently never
called indirectly.
The problem was again iterating over pointer sets. This produced non-deterministic use list orderings, which does result in non-deterministic code generation itself.
But somehow debug info generation depends on the use list ordering (which should be investigated why).
And the non-deterministic debug info changes triggered re-running of the llvm pipeline.
To do so this commit does a few different things:
1. I changed SILOptFunctionBuilder to notify the pass manager's logging
functionality when new functions are added to the module and to notify analyses
as well. NOTE: This on purpose does not put the new function on the pass manager
worklist since we do not want to by mistake introduce a large amount of
re-optimizations. Such a thing should be explicit.
2. I eliminated SILModuleTransform::notifyAddFunction. This just performed the
operations from 1. Now that SILOptFunctionBuilder performs this operation for
us, it is not needed.
3. I changed SILFunctionTransform::notifyAddFunction to just add the function to
the passmanager worklist. It does not need to notify the pass manager's logging
or analyses that a new function was added to the module since
SILOptFunctionBuilder now performs that operation. Given its reduced
functionality, I changed the name to addFunctionToPassManagerWorklist(...). The
name is a little long/verbose, but this is a feature since one should think
before getting the pass manager to rerun transforms on a function. Also, giving
it a longer name calls out the operation in the code visually, giving this
operation more prominance when reading code. NOTE: I did the rename using
Xcode's refactoring functionality!
rdar://42301529
This works around a potential circular dependence issue where TypeSubstCloner
needs access to SILOptFunctionBuilder but is in libswiftSIL.
rdar://42301529
I am going to add the code in a bit that does the notifications. I tried to pass
down the builder instead of the pass manager. I also tried not to change the
formatting.
rdar://42301529
Remove all the bespoke utilities for handling apply sites.
Use the ApplySite abstraction instead. As a side effect,
we can now properly analyze begin_apply and try_apply.
Fixes <rdar://problem/42815224> Swift CI: 2. Swift Source Compatibility Suite (master):
Kitura project fails with compiler crash
This will be properly fixed by rewriting the AllocBoxToStack utilities
in a follow up commit.
This was undoing the effects of 36eae9d4f, which is supposed to
guarantee trapping behavior on unexpected values. Worse, it put
switches that *actually had defaults* back into
undefined-behavior-land if a case were added.
The verifier changes are in lieu of test changes; this was originally
caught by IRGen/CoreGraphics_test.swift.
rdar://problem/42775178
This commit does not modify those APIs or their usage. It just:
1. Moves the APIs onto SILFunctionBuilder and makes SILFunctionBuilder a friend
of SILModule.
2. Hides the APIs on SILModule so all users need to use SILFunctionBuilder to
create/destroy functions.
I am doing this in order to allow for adding/removing function notifications to
be enforced via the type system in the SILOptimizer. In the process of finishing
off CallerAnalysis for FSO, I discovered that we were not doing this everywhere
we need to. After considering various other options such as:
1. Verifying after all passes that the notifications were sent correctly and
asserting. Turned out to be expensive.
2. Putting a callback in SILModule. This would add an unnecessary virtual call.
I realized that by using a builder we can:
1. Enforce that users of SILFunctionBuilder can only construct composed function
builders by making the composed function builder's friends of
SILFunctionBuilder (notice I did not use the word subclass, I am talking
about a pure composition).
2. Refactor a huge amount of code in SILOpt/SILGen that involve function
creation onto a SILGenFunctionBuilder/SILOptFunctionBuilder struct. Many of
the SILFunction creation code in question are straight up copies of each
other with small variations. A builder would be a great way to simplify that
code.
3. Reduce the size of SILModule.cpp by 25% from ~30k -> ~23k making the whole
file easier to read.
NOTE: In this commit, I do not hide the constructor of SILFunctionBuilder since
I have not created the derived builder structs yet. Once I have created those in
a subsequent commit, I will hide that constructor.
rdar://42301529
At least most of these were latent bugs since the code was
unreachable in the PartialApply case. But that's no excuse to misuse
the API.
Also, whenever referring to an integer index, be explicit about
whether it is an applied argument or callee argument.
The reason to do this is that otherwise, retain code motion can increase the
size of the IR by large amounts as it pushes tons and tons of retains/releases
into these sorts of blocks. One one test case, it increased the amount of raw
instructions by 2-3 orders of magnitude.
We used to implement this in ARCSequenceOpts when it did code motion. When code
motion was stripped out of ARCSequenceOpts and ARCCodeMotion was implemented,
this was not implemented for some reason yielding this regression.
NOTE: If one reads this PR there is "retain" trimming code as well as usage of
ProgramTerminationAnalysis. The reason why I implemented the code in this way is
that tThe program termination analysis allows us to avoid inserting retains at
all into fatal error blocks, but it can not handle cases where a fatal error
block has been merged into another block. The trimmer code handles this case and
potential cases where side-effect code is in the fatal error block. On the other
hand, if we just relied on the retain trimming code, we would be inserting a
huge amount of retains just to free them. That is really wasteful from a
performance standpoint given the amount of retains that we can insert here.
rdar://42347024
For now, the accessors have been underscored as `_read` and `_modify`.
I'll prepare an evolution proposal for this feature which should allow
us to remove the underscores or, y'know, rename them to `purple` and
`lettuce`.
`_read` accessors do not make any effort yet to avoid copying the
value being yielded. I'll work on it in follow-up patches.
Opaque accesses to properties and subscripts defined with `_modify`
accessors will use an inefficient `materializeForSet` pattern that
materializes the value to a temporary instead of accessing it in-place.
That will be fixed by migrating to `modify` over `materializeForSet`,
which is next up after the `read` optimizations.
SIL ownership verification doesn't pass yet for the test cases here
because of a general fault in SILGen where borrows can outlive their
borrowed value due to being cleaned up on the general cleanup stack
when the borrowed value is cleaned up on the formal-access stack.
Michael, Andy, and I discussed various ways to fix this, but it seems
clear to me that it's not in any way specific to coroutine accesses.
rdar://35399664
The other side of #17404. Since we don't want to generate up front key path metadata for properties/subscripts with no withheld implementation details, the client should generate a key path component that can be used to represent a key path component based on its public interface.