Previously, findGuaranteedReferenceRoots always stopped searching when
finding a begin_borrow, because it's not an ownership-forwarding
instruction. Here, it is conditionally allowed to keep search through
the borrowee of that begin_borrow if it itself is guaranteed.
`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.
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.
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
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.
Previously, when encountering a borrow of a guaranteed value, the
end_borrows of that reborrow were marked alive. Only doing that enables
end_borrows of the outer borrow scope can be marked as dead. The result
is that uses of the reborrowed value (including its end_borrow) can
outstrip the outer borrow scope, which is illegal.
Here, the outer borrow scope's end_borrows are marked alive. To do
that, the originally borrowed values have to be identified via
findGuaranteedReferenceRoots.
If a phi argument is dead and it reborrowing it was dependent on some
other value, that other value on which it was dependent may have already
itself been deleted. In that case, the destroy_value would have been
added just before the terminator of the predecessors of the block which
contained the dead phi. So, when deciding where to insert the
end_borrow, iterate backwards from the end of the block, skipping the
terminator updating the insertion point every time a destroy_value
instruction is encountered until we hit an instruction with a different
opcode. This ensures that no matter how many destroy_values may have
been added just before the terminator, the end_borrow will preceed them.
This commit just tweaks the preexisting logic that checked for this
condition. Specifically, the previous code didn't handle the case where
the block contains only a terminator and a destroy_value.
SROA and Mem2Reg now can leverage DIExpression -- op_fragment, more
specifically -- to generate correct debug info for optimized SIL. Some
important highlights:
- The new swift::salvageDebugInfo, similar to llvm::salvageDebugInfo,
tries to restore / transfer debug info from a deleted instruction.
Currently I only implemented this for store instruction whose
destination is an alloc_stack value.
- Since we now have source-variable-specific SIL location inside a
`debug_value` instruction (and its friends), this patch teaches
SILCloner and SILInliner to remap the debug scope there in addition
to debug scope of the instruction.
- DCE now does not remove `debug_value` instruction whose associating
with a function argument SSA value that is not used elsewhere. Since
that SSA value will not disappear so we should keep the debug info.
Unless the type is a trivial scalar. As a follow-up we could try to
prove that the referenced object is unique, but that seems more
natural for DeadObjectElimination.
This optimization was never correct because references may alias. See
rdar://36038096 ([SR-6608] DeadCodeElimination removes fix_lifetime
instructions.)
Fixes rdar://74759728 (The compiler/optimizer seems to be shortening
the lifetime too early)
A reborrow's base value may change if the base value is also passed as
an operand on a branch. Record reborrow dependencies so that we can mark
the base value as live, if the reborrow was also live. This ensures we
do not insert destroy_value on the base value prematurely before the
lifetime of the reborrow ends.
This removes the ambiguity when casting from a SingleValueInstruction to SILNode, which makes the code simpler. E.g. the "isRepresentativeSILNode" logic is not needed anymore.
Also, it reduces the size of the most used instruction class - SingleValueInstruction - by one pointer.
Conceptually, SILInstruction is still a SILNode. But implementation-wise SILNode is not a base class of SILInstruction anymore.
Only the two sub-classes of SILInstruction - SingleValueInstruction and NonSingleValueInstruction - inherit from SILNode. SingleValueInstruction's SILNode is embedded into a ValueBase and its relative offset in the class is the same as in NonSingleValueInstruction (see SILNodeOffsetChecker).
This makes it possible to cast from a SILInstruction to a SILNode without knowing which SILInstruction sub-class it is.
Casting to SILNode cannot be done implicitly, but only with an LLVM `cast` or with SILInstruction::asSILNode(). But this is a rare case anyway.
`get_async_continuation[_addr]` begins a suspend operation by accessing the continuation value that can resume
the task, which can then be used in a callback or event handler before executing `await_async_continuation` to
suspend the task.
The cast optimizer was too eager to fold casts where the source and
target lowered types were the same, even though the formal types
might be different. Move the classifyFeasibility() check to deal
with this case.
Also in dead code elimination we have to mark all operands of a
cast branch instruction as live, not just the first operand,
otherwise we miss the special 'type dependent' self metadata
operand and replace it with 'undef'.
The XXOptUtils.h convention is already established and parallels
the SIL/XXUtils convention.
New:
- InstOptUtils.h
- CFGOptUtils.h
- BasicBlockOptUtils.h
- ValueLifetime.h
Removed:
- Local.h
- Two conflicting CFG.h files
This reorganization is helpful before I introduce more
utilities for block cloning similar to SinkAddressProjections.
Move the control flow utilies out of Local.h, which was an
unreadable, unprincipled mess. Rename it to InstOptUtils.h, and
confine it to small APIs for working with individual instructions.
These are the optimizer's additions to /SIL/InstUtils.h.
Rename CFG.h to CFGOptUtils.h and remove the one in /Analysis. Now
there is only SIL/CFG.h, resolving the naming conflict within the
swift project (this has always been a problem for source tools). Limit
this header to low-level APIs for working with branches and CFG edges.
Add BasicBlockOptUtils.h for block level transforms (it makes me sad
that I can't use BBOptUtils.h, but SIL already has
BasicBlockUtils.h). These are larger APIs for cloning or removing
whole blocks.
The ownership kind is Any for trivial types, or Owned otherwise, but
whether a type is trivial or not will soon depend on the resilience
expansion.
This means that a SILModule now uniques two SILUndefs per type instead
of one, and serialization uses two distinct sentinel IDs for this
purpose as well.
For now, the resilience expansion is not actually used here, so this
change is NFC, other than changing the module format.
This disables a bunch of passes when ownership is enabled. This will allow me to
keep transparent functions in ossa and skip most of the performance pipeline without
being touched by passes that have not been updated for ownership.
This is important so that we can in -Onone code import transparent functions and
inline them into other ossa functions (you can't inline from ossa => non-ossa).