The SIL optimizer has fundamental bugs that result in dropping non-Copyable
struct & enum the deinitializers.
Fix this by
1. correctly representing the ownership of struct & enum values that are
initialized from trivial values.
2. checking move-only types before deleting forwarding instructions.
These bugs block other bug fixes. They are exposed by other unrelated SIL
optimizations to SIL. I'm sure its possible to expose the bugs with source-level
tests, but the current order of inlining and deinit devirtualization has been
hiding the bugs and complicates reproduction.
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)
drop_deinit forwards ownership while effectively stripping the deinitializer. It is similar to a type cast.
Fixes rdar://125590074 ([NonescapableTypes] Nonescapable types
cannot have deinits)
When a load/load_borrow is canonicalized, canonicalization should
continue from where the load was before rather than the first
instruction that was after it. Enables narrowing the load through
a sequence of projections.
We want to preserve the borrow scope during switch dispatch so that move-only
checking doesn't try to analyze destructures or consumes out of it. SILGen
should mark anywhere that's a potential possibility with its own marker so that
it gets borrow checked independently.
Previously, the lexical attribute on begin_borrow instructions was used.
This doesn't work for values without lexical lifetimes which are
consumed, e.g. stdlib CoW types. Here, the new var_decl attribute on
begin_borrow is keyed off of instead. This flag encodes exactly that a
value corresponds to a source-level VarDecl, which is the condition
under which checking needs to run.
rdar://118059326
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".
I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
Fixes a know issue in which load splitting drops debug info.
After splitting the load, create a new debug_value instruction for the
loaded memory location, inserting a dereference expression if needed.
This fixes debug information for -Onone debugging. Later, after fixing
optimizations, it will also be called at -O, but not on 5.9.
Fixes rdar://104700920 (At -Onone preserve debug info after splitting loads)
Issue: LLDB missing variables in certain case #62241
Before deleting a load, simply rewrite its debug info to refer to the
loaded address:
%l = load %a
debug_value %l, loc0, scope0, var0
--->
debug_value %a, loc0, scope0, var0, expr op_deref
%l = load %a
Note that alloc_stack addresses do not require the addition of
op_deref because they are already special-cased in IRGen.
This will be called from salvageDebugInfo when it is supported by
optmizations. Until then, it is only called selectively at -Onone.
Load splitting converts an aggregate load into a set of subobject
loads. This is required at -Onone for exclusivity diagnostics.
We cannot preserve the original debug information by redirecting debug
info to the memory address, because that might result in incorrect
debug values if the memory is reused.
Before this fix, we "conservatively" drop debug info in those
cases. This fix preserves full debug info by keeping the original
aggregate load intact alongside the new subobject loads. To avoid
exclusivity violations, it create a new unsafe access scope for the
old load.
Fixes LLDB missing variables in certain case #62241
`getValue` -> `value`
`getValueOr` -> `value_or`
`hasValue` -> `has_value`
`map` -> `transform`
The old API will be deprecated in the rebranch.
To avoid merge conflicts, use the new API already in the main branch.
rdar://102362022
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.
Debug information needs to be preserved at -Onone.
And it should be preserved to a reasonable extend at -O, not
completely thrown away.
This transformation does neither.
The -Onone case is more important. The FIXME explains how that could
be done.
I'm including an example of how to handle the -O case, currently under
a a flag, -sil-load-splitting-debug-info.
This -O support uses debug fragments, which currently crashes when
building SwiftPM's PackageModel module in LLVM's 'X86 Assembly
Printer', llvm::DwarfExpression::addFragmentOffset.
We don't appear to have any verification of debug fragment in either
SIL or LLVM that would catch this. And I haven't been able to reduce
the problem to a small test case.
The effect of passing -enable-copy-propagation is both to enable the
CopyPropagation pass to shorten object lifetimes and also to enable
lexical lifetimes to ensure that object lifetimes aren't shortened while
a variable is still in scope and used.
Add a new flag, -enable-lexical-borrow-scopes=true to override
-enable-copy-propagation's effect (setting it to ::ExperimentalLate) on
SILOptions::LexicalLifetimes that sets it to ::Early even in the face of
-enable-copy-propagation. The old flag -disable-lexical-lifetimes is
renamed to -enable-lexical-borrow-scopes=false but continues to set that
option to ::Off even when -enable-copy-propagation is passed.
Previously, CanonicalizeInstruction::eliminateSimpleBorrows bailed out
when encountering any any [lexical] begin_borrow. While generally such
borrow scopes must be preserved, they may be removed if they are
redundant (when the borrowed value is guaranteed by another means: an
outer lexical borrow scope or a guaranteed function argument). Here,
removing such redundant lexical borrow scopes is enabled.
This API is useful when writing compiler code that needs to handle ossa/non-ossa
as well as load_borrow/load while in OSSA. I am going to use this in
SILMem2Reg.
Currently all of these places in the code base perform simplifyInstruction and
then a replaceAllSimplifiedUsesAndErase(...). This is a bad pattern since:
1. simplifyInstruction assumes its result will be passed to
replaceAllSimplifiedUsesAndErase. So by leaving these as separate things, we
allow for users to pointlessly make this mistake.
2. I am going to implement in a subsequent commit a utility that lifetime
extends interior pointer bases when replacing an address with an interior
pointer derived address. To do this efficiently, I want to reuse state I
compute during simplifyInstruction during the actual RAUW meaning that if the
two operations are split, that is difficult without extending the API. So by
removing this, I can make the transform and eliminate mistakes at the same
time.
Instead, I just added some static helper methods that perform the same
operations without needing to deal with generics/etc on OwnershipForwardingMixin
itself. The reason why I did this is that this Mixin is not part of the SILNode
heirarchy so we shouldn't use utilities tied to the SILNode hierarchy.
Specifically before this PR, if a caller did not customize a specific callback
of InstModCallbacks, we would store a static default std::function into
InstModCallbacks. This means that we always would have an indirect jump. That is
unfortunate since this code is often called in loops.
In this PR, I eliminate this problem by:
1. I made all of the actual callback std::function in InstModCallback private
and gave them a "Func" postfix (e.x.: deleteInst -> deleteInstFunc).
2. I created public methods with the old callback names to actually call the
callbacks. This ensured that as long as we are not escaping callbacks from
InstModCallback, this PR would not result in the need for any source changes
since we are changing a call of a std::function field to a call to a method.
3. I changed all of the places that were escaping inst mod's callbacks to take
an InstModCallback. We shouldn't be doing that anyway.
4. I changed the default value of each callback in InstModCallbacks to be a
nullptr and changed the public helper methods to check if a callback is
null. If the callback is not null, it is called, otherwise the getter falls
back to an inline default implementation of the operation.
All together this means that the cost of a plain InstModCallback is reduced and
one pays an indirect function cost price as one customizes it further which is
better scalability.
P.S. as a little extra thing, I added a madeChange field onto the
InstModCallback. Now that we have the helpers calling the callbacks, I can
easily insert instrumentation like this, allowing for users to pass in
InstModCallback and see if anything was RAUWed without needing to specify a
callback.
E.x.:
```
%1 = unchecked_ref_cast %0
destroy_value %1
->
destroy_value %0
```
I am doing this only in non-Raw SIL to ensure that I do not disturb the
mandatory passes.
This is a generic API that when ownership is enabled allows one to replace all
uses of a value with a value with a differing ownership by transforming/lifetime
extending as appropriate.
This API supports all pairings of ownership /except/ replacing a value with
OwnershipKind::None with a value without OwnershipKind::None. This is a more
complex optimization that we do not support today. As a result, we include on
our state struct a helper routine that callers can use to know if the two values
that they want to process can be handled by the algorithm.
My moticiation is to use this to to update InstSimplify and SILCombiner in a
less bug prone way rather than just turn stuff off.
Noting that this transformation inserts ownership instructions, I have made sure
to test this API in two ways:
1. With Mandatory Combiner alone (to make sure it works period).
2. With Mandatory Combiner + Semantic ARC Opts to make sure that we can
eliminate the extra ownership instructions it inserts.
As one can see from the tests, the optimizer today is able to handle all of
these transforms except one conditional case where I need to eliminate a dead
phi arg. I have a separate branch that hits that today but I have exposed unsafe
behavior in ClosureLifetimeFixup that I need to fix first before I can land
that. I don't want that to stop this PR since I think the current low level ARC
optimizer may be able to help me here since this is a simple transform it does
all of the time.
I am going to be using in inst-simplify/sil-combine/canonicalize instruction a
RAUW everything against everything API (*). This creates some extra ARC
traffic/borrows. It is going to be useful to have some simple peepholes that
gets rid of some of the extraneous traffic.
(*) Noting that we are not going to support replacing non-trivial
OwnershipKind::None values with non-trivial OwnershipKind::* values. This is a
corner case that only comes up with non-trivial enums that have a non-payloaded
or trivial enum case. It is much more complex to implement that transform, but
it is an edge case, so we are just not going to support those for now.
----
I also eliminated the dependence of SILGenCleanup on Swift/SwiftShims. This
speeds up iterating on the test case with a debug compiler since we don't need
those modules.
* Fixes loadable edge case for address-only types.
* Re-work, generalize, and simplify by using projections.
* Support `-enable-cxx-interop` in sil-opt.
This improves on the previous situation:
- The request ensures that the backing storage for lazy properties
and property wrappers gets synthesized first; previously it was
only somewhat guaranteed by callers.
- Instead of returning a range this just returns an ArrayRef,
which simplifies clients.
- Indexing into the ArrayRef is O(1), which addresses some FIXMEs
in the SIL optimizer.
This is the complement to load canonicalization. Although store
canonicalization is not required before diagnostics, it should be
defined in the same utility.
This adds support to the load->struct_extract canonicalization for
nontrivial element types which look like:
load [copy]
borrow
struct_extract
...uses...
end_borrow
destroy
CanonicalizeInstruction will be a superset of
simplifyInstruction (once all the transforms are fixed for ownership
SIL). Additionally, it will also include simple SSA-based
canonicalization that requires new instruction creation. It may not
perform any optimization that interferes with diagnostics or increases
compile time.
Canonicalization replaces simplifyInstruction in SILCombine so we can
easily factor some existing SILCombine transforms into canonicalization.