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)
Salvage Debug Info was only being called for simplifications, and not
for function passes written in Swift. Salvage Debug Info is now called
for both cases.
Introduce two modes of bridging:
* inline mode: this is basically how it worked so far. Using full C++ interop which allows bridging functions to be inlined.
* pure mode: bridging functions are not inlined but compiled in a cpp file. This allows to reduce the C++ interop requirements to a minimum. No std/llvm/swift headers are imported.
This change requires a major refactoring of bridging sources. The implementation of bridging functions go to two separate files: SILBridgingImpl.h and OptimizerBridgingImpl.h.
Depending on the mode, those files are either included in the corresponding header files (inline mode), or included in the c++ file (pure mode).
The mode can be selected with the BRIDGING_MODE cmake variable. By default it is set to the inline mode (= existing behavior). The pure mode is only selected in certain configurations to work around C++ interop issues:
* In debug builds, to workaround a problem with LLDB's `po` command (rdar://115770255).
* On windows to workaround a build problem.
* add `GlobalVariable.staticInitializerInstructions` to access all initializer instructions of a global
* implement `GlobalVariable.staticInitValue` with `GlobalVariable.staticInitializerInstructions`
* this requires that `InstructionList.reversed()` works without accessing the parent block of the iterator instruction
* allow `Context.erase(instruction:)` to delete instructions from a global's initializer list, which means to handle the case where a deleted instruction has no parent function.
llvm::SmallSetVector changed semantics
(https://reviews.llvm.org/D152497) resulting in build failures in Swift.
The old semantics allowed usage of types that did not have an
`operator==` because `SmallDenseSet` uses `DenseSetInfo<T>::isEqual` to
determine equality. The new implementation switched to using
`std::find`, which internally uses `operator==`. This type is used
pretty frequently with `swift::Type`, which intentionally deletes
`operator==` as it is not the canonical type and therefore cannot be
compared in normal circumstances.
This patch adds a new type-alias to the Swift namespace that provides
the old semantic behavior for `SmallSetVector`. I've also gone through
and replaced usages of `llvm::SmallSetVector` with the
`Swift::SmallSetVector` in places where we're storing a type that
doesn't implement or explicitly deletes `operator==`. The changes to
`llvm::SmallSetVector` should improve compile-time performance, so I
left the `llvm::SmallSetVector` where possible.
APIs on ForwardingInstruction should be written as static taking in
a SILInstruction as a parameter making it awkward.
Introduce a ForwardingOperation wrapper type and move the apis from the
old "mixin" class to the wrapper type.
Add new api getForwardedOperands()
Previously, the utility bailed out on lexical lifetimes because it
didn't respect deinit barriers. Here, deinit barriers are found and
added to liveness if the value is lexical. This enables copies to be
propagated without hoisting destroys over deinit barriers.
rdar://104630103
* for testing: add the option `-simplify-instruction=<instruction-name>` to only run simplification passes for that instruction type
* on the swift side, add `Options.enableSimplification`
* split the `PassContext` into multiple protocols and structs: `Context`, `MutatingContext`, `FunctionPassContext` and `SimplifyContext`
* change how instruction passes work: implement the `simplify` function in conformance to `SILCombineSimplifyable`
* add a mechanism to add a callback for inserted instructions
To improve the debugging experience of values whose lifetimes are
canonicalized without compromising the semantics expressed in the source
language, when canonicalizing OSSA lifetimes at Onone, lengthen
lifetimes as much as possible without incurring copies that would be
eliminated at O.
rdar://99618502
Andy some time ago already created the new API but didn't go through and update
the old occurences. I did that in this PR and then deprecated the old API. The
tree is clean, so I could just remove it, but I decided to be nicer to
downstream people by deprecating it first.
* 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`.
And a few other small related changes:
* remove libswiftPassInvocation from SILInstructionWorklist (because it's not needed)
* replace start/finishPassRun with start/finishFunction/InstructionPassRun
NFC
Required before fixing/re-enabling OSSA RAUW utilities.
Make sure the SILCombine worklist canonicalizes all the copies and
guarantees termination.
Run canonicalization on every existing copy_value once
and once for every new copy_value added during SILCombine.
Only add copies and their uses back to the worklist if
canonicalization deleted an instruction.
Add tracing for sinking forwaring instructions.
And fix the way it handles of borrow scopes so we can enable borrow
scope rewiting. Make sure SILCombine only does canonicalization that
operates on a self-contained single-value-lifetime. It's important to
limit SILCombine to transformations where each individual step
converges quickly to a more canonical form. Rewriting borrow scopes
requires the copy propagation pass to coordinate all the individual
transformations.
Make canonicalizeLifetimes a SILCombine utility. This moves complexity
out of the main loop. SILCombine knows which values it wants to
canonicalize and can directly call either canonicalizeValueLifetime or
canonicalizeFunctionArgument for each one.
Respect the -enable/disable-copy-propagation options.
Instruction passes are basically visit functions in SILCombine for a specific instruction type.
With the macro SWIFT_INSTRUCTION_PASS such a pass can be declared in Passes.def.
SILCombine then calls the run function of the pass in libswift.
Track in-use iterators and update them both when instructions are
deleted and when they are added.
Safe iteration in the presence of arbitrary changes now looks like
this:
for (SILInstruction *inst : deleter.updatingRange(&bb)) {
modify(inst);
}
It's not needed anymore with delayed instruction deletion.
It was used for two purposes:
1. For analysis, which cache instructions, to avoid dangling instruction pointers
2. For passes, which maintain worklists of instructions, to remove a deleted instructions from the worklist. This is now done by checking SILInstruction::isDeleted().
Instead of caching alias results globally for the module, make AliasAnalysis a FunctionAnalysisBase which caches the alias results per function.
Why?
* So far the result caches could only grow. They were reset when they reached a certain size. This was not ideal. Now, they are invalidated whenever the function changes.
* It was not possible to actually invalidate an alias analysis result. This is required, for example in TempRValueOpt and TempLValueOpt (so far it was done manually with invalidateInstruction).
* Type based alias analysis results were also cached for the whole module, while it is actually dependent on the function, because it depends on the function's resilience expansion. This was a potential bug.
I also added a new PassManager API to directly get a function-base analysis:
getAnalysis(SILFunction *f)
The second change of this commit is the removal of the instruction-index indirection for the cache keys. Now the cache keys directly work on instruction pointers instead of instruction indices. This reduces the number of hash table lookups for a cache lookup from 3 to 1.
This indirection was needed to avoid dangling instruction pointers in the cache keys. But this is not needed anymore, because of the new delayed instruction deletion mechanism.
To be more explicit, canonicalizeOSSALifetimes is a utility that
re-canonicalizes all at once a set of defs that the caller found by applying
CanonicalizeOSSALifetime::getCanonicalCopiedDef(copy)). The reason why I am
doing this is that when we RAUW in OSSA, we sometimes insert additional copies
to make the problem easier for a utility to handle. This lets us canonicalize
away any copies before we even leave the pass.
... with a fix for a non-assert build crash: I used the wrong ilist type for SlabList. This does not explain the crash, though. What I think happened here is that llvm miscompiled and put the llvm_unreachable from the Slab's deleteNode function unconditionally into the SILModule destructor.
Now by using simple_ilist, there is no need for a deleteNode at all.