-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SILOpt] Hoist destroys for non-lexical alloc_stacks more aggressively. #60478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SILOpt] Hoist destroys for non-lexical alloc_stacks more aggressively. #60478
Conversation
@swift-ci please benchmark |
@swift-ci please smoke test macOS platform |
d696bb4
to
ad6f47c
Compare
@swift-ci please test |
@swift-ci please benchmark |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thanks.
worklist.initializeRange(ABI->getUses()); | ||
while (auto *use = worklist.pop()) { | ||
// See through mark_uninitialized instructions. | ||
if (auto *mui = dyn_cast<MarkUninitializedInst>(use->getUser())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hard-coding opcodes here (for mark_uninitialized) creates an implicit correctness dependency on SIL design. How will we find out if SILGen ever introduces new instructions in the same position as mark_uninitialized? Maybe we want a flag on alloc_box just at least as a verification mechanism?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added verification that begin_borrow [lexical]
s whose operand's type is a SILBoxType
is either an AllocBoxInst
or a SILFunctionArgument
(looking through copy_value
s, begin_borrow
s, and mark_uninitialized
instructions) in 3df609f .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AllocBoxToStack also now looks through to the uses of non-lexical begin_borrows to properly handle the case of repeated inlining where the first callee's SILFunctionArgument is non-lexical but the second callee's SILFunctionArgument is lexical in 896a464 .
ad6f47c
to
fdb20e1
Compare
Previously, whenever an alloc_box that was promoted to an alloc_stack, the new alloc_stack would be lexical. The result was that alloc_boxes which didn't need (or explicitly didn't want, in the case of eager move vars) received lexical alloc_stacks. Here, only add the lexical flag to the new alloc_stack instruction if any of the box's uses are `begin_borrow [lexical]`. That way, alloc_boxes end up having lexical alloc_stacks only if they were "lexical alloc_boxes".
When inlining a function to which an alloc_stack is passed (via any convention that's not all of mutating, exclusive, and inout), mark the alloc_stack as lexical. Otherwise, destroy_addrs could be hoisted through the inlined function in a way inconsistent with how they were hoisted through the callee prior to inlining.
Previously, whenever an alloc_stack [lexical] was seen, optimization bailed conservatively. In the fullness of time, we should optimize an alloc_stack [lexical] so long as: (1) the root of the source of the store/copy_addr is lexical (2) the range in which the alloc_stack [lexical] contains the value store'd/copy_addr'd into it (i.e. the range ending at destroy_addr/etc) is contained within the live range of that lexical root Here, an incremental step in that direction is taken: if the base of the accessed storage of the source of a copy_addr is a guaranteed function argument, then we know (1) the base is lexical--guaranteed function arguments are always lexical (2) the range in which the alloc_stack contains the value copy_addr'd in is trivially contained within the range of that lexical root--the live range of a guaranteed function argument is the whole function Added TODOs for the full optimization.
Require that lexical borrows of values which are instances of some SILBoxType guard the lifetime of vars or closure captures; that's to say, of AllocBoxInsts and SILFunctionArguments. Look through a few instructions types to find those: - copy_value - inserted by SILGen - begin_borrow - inserted during inlining - mark_uninitialized - inserted by SILGen
fdb20e1
to
3df609f
Compare
@swift-ci please test |
@swift-ci please test source compatibility |
Previously, when hoisting
destroy_addr
s foralloc_stack
s, deinit barriers were always considered barriers to hoisting. Here, that is changed so that hoisting only is barred when thealloc_stack
whosedestroy_addr
is being hoisted is lexical.Additionally, more non-lexical
alloc_stack
s are formed: onlyalloc_box
es whose lifetimes are protected via a lexical borrow scope get promoted to lexicalalloc_stack
s. This will become more significant when types and values are annotated@_eagerMove
.In order to do this safely, we must ensure that implicit lexical lifetimes are not removed. Address function arguments with a convention other than Indirect_Inout have implicit lexical lifetimes: e.g. SSADestroyHoisting respects deinit barriers when hoisting the the destroy_addrs of such arguments. Until we have
begin_borrow_addr
/end_borrow_addr
, when inlining functions with such arguments, if the address passed to it has analloc_stack
as its storage base, mark thatalloc_stack
lexical.