This fix unblocks unrelated optimizer commits. A unit test will be
introduced with those commits.
The RAUW utility does not correctly handle reborrows. It is in the
middle of being rewritten. For now, simply bail out since this isn't
an important case to optimize.
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.
Add NonTypeDependentOperandToValue predicate for composability.
Add a getNonTypeDependentOperandValues(), which can be used as a functor.
The skipTypeDependentOperands parameter complicated the API.
Previously, in GatherUniqueStorageUses::visiteUse, the results of calls
to visit#### on the visitor were not used. Here, they are returned from
the function.
This is an instruction that I am going to use to drive some of the ownership
based dataflow optimizations that I am writing now. The instruction contains a
kind that allows one to know what type of checking is required and allows the
need to add a bunch of independent instructions for independent checkers. Each
checker is responsible for removing all of its own mark instructions. NOTE:
MarkMustCheckInst is only allowed in Raw SIL since once we are in Canonical SIL
we want to ensure that all such checking has already occurred.
Fix an obvious mistake that happens to not break anything in practice.
Note that the argument index of a terminator result is not the same as
an operand index for the terminator instruction.
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
Store a list of argument effects in a function, which specify if and how arguments escape.
Such effects can be specified in the Swift source code (for details see docs/ReferenceGuides/UnderscoredAttributes.md) or derived in an optimization pass.
For details see the documentation in SwiftCompilerSources/Sources/SIL/Effects.swift.
Introduce a new instruction `dealloc_stack_ref ` and remove the `stack` flag from `dealloc_ref`.
The `dealloc_ref [stack]` was confusing, because all it does is to mark the deallocation of the stack space for a stack promoted object.
There are three major changes here:
1. The addition of "SILFunctionTypeRepresentation::CXXMethod".
2. C++ methods are imported with their members *last*. Then the arguments are switched when emitting the IR for an application of the function.
3. Clang decls are now marked as foreign witnesses.
These are all steps towards being able to have C++ protocol conformance.
Analyze and classify the leaf uses of unique storage.
Storage that has a unique set of roots within this function includes
alloc_stack, alloc_box, exclusive argument, and global variables. All access
to the storage within this function is derived from these roots.
Gather the kinds of uses that are typically relevant to algorithms:
- loads (including copies out of, not including inout args)
- stores (including copies into and inout args)
- destroys (of the entire aggregate)
- debugUses (only populated when preserveDebugInfo == false)
Replaced findInnerTransitiveGuaranteeedUsesOfBorrowedValue with
findExtendedUsesOfSimpleBorrowedValue. Starting from a borrowed value,
it finds all extended (i.e., seeing through copies) uses of the borrow
and its projections within the simple (i.e. without considering
reborrowing) borrow scope.
More bugs related to dead-end blocks and OSSA lifetimes that aren't
well formed because of that.
Independently, I'm working on prohibiting ill-formed OSSA even in the
presence of dead-end blocks. That makes these workarounds unnecessary,
but we urgently need a narrow fix here.
SemanticARCOptVisitor::performGuaranteedCopyValueOptimization was
converting this SIL
%borrow = begin_borrow %copiedValue
%copy = copy_value %borrow
%borrowCopy = begin_borrow %copy
end_borrow %borrow
end_borrow %borrowCopy
destroy_value %copy
// something something
unreachable
into
%borrow = begin_borrow %copiedValue
%innerBorrow = begin_borrow %borrow
end_borrow %borrow
end_borrow %innerBorrow
// something something
unreachable
Dead-end blocks are simply irrelevant for this
optimization. Unfortunately, there were multiple layers of attempted
workarounds that were hiding the real problem, except in rare cases.
Thanks Nate Chandler for reducing the test.
During copy propagation (for which -enable-copy-propagation must still
be passed), also try to shrink borrow scopes by hoisting end_borrows
using the newly added ShrinkBorrowScope utility.
Allow end_borrow instructions to be hoisted over instructions that are
not deinit barriers for the value which is borrowed. Deinit barriers
include uses of the value, loads of memory, loads of weak references
that may be zeroed during deinit, and "synchronization points".
rdar://79149830
This instruction is similar to a copy_addr except that it marks a move of an
address that has to be checked. In order to keep the memory lifetime verifier
happy, the semantics before the checker runs are the mark_unresolved_move_addr is
equivalent to copy_addr [init] (not copy_addr [take][init]).
The use of this instruction is that Mandatory Inlining converts builtin "move"
to a mark_unresolved_move_addr when inlining the function "_move" (the only
place said builtin is invoked).
This is then run through a special checker (that is later in this PR) that
either proves that the mark_unresolved_move_addr can actually be a move in which
case it converts it to copy_addr [take][init] or if it can not be a move, emit
an error and convert the instruction to a copy_addr [init]. After this is done
for all instructions, we loop back through again and emit an error on any
mark_unresolved_move_addr that were not processed earlier allowing for us to
know that we have completeness.
NOTE: The move kills checker for addresses is going to run after Mandatory
Inlining, but before predictable memory opts and friends.