-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Support @guaranteed forwarding phis #61505
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
Conversation
8686d06
to
14fbee4
Compare
@swift-ci test |
@swift-ci test source compatibility |
@swift-ci benchmark |
14fbee4
to
c0bf82e
Compare
@swift-ci test |
c0bf82e
to
ff3d553
Compare
@swift-ci test |
ff3d553
to
598119b
Compare
@swift-ci test |
598119b
to
9cb83c3
Compare
@swift-ci test |
9cb83c3
to
c99d928
Compare
@swift-ci test Linux platform |
@swift-ci test macOS platform |
@swift-ci benchmark |
@swift-ci test source compatibility |
lib/SIL/IR/OperandOwnership.cpp
Outdated
if (destBlockArgOwnershipKind == OwnershipKind::Guaranteed) { | ||
return OperandOwnership::Reborrow; | ||
auto phiOp = getValue(); |
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.
I wonder why you can't just call isGuaranteedForwardingPhi
here instead of duplicating all the logic.
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.
Fixed.
@@ -339,6 +345,17 @@ bool swift::findExtendedUsesOfSimpleBorrowedValue( | |||
recordUse(use); | |||
break; | |||
|
|||
case OperandOwnership::GuaranteedForwardingPhi: { |
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.
findExtendedUsesOfSimpleBorrowedValue
is finding "copy-extended" uses within the "simple" borrow scope. It doesn't look past reborrows. I think it expects all uses to be dominated by borrowedValue
.
So I think it should only follow the uses of a guaranteed phi if that phi is dominated by borrowedValue
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.
findExtendedUsesOfSimpleBorrowedValue
is just recording the use of Reborrow
, and the clients are bailing out if there is a Reborrow
before calling this api. I changed findExtendedUsesOfSimpleBorrowedValue
to return false
when there is a Reborrow
(similar to findUsesOfSimpleValue
). With that, all collected GuranteedForwardingPhi
ops will have a dominated borrow root.
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.
In OperandOwnership::GuaranteedForwardingPhi: {
please comment "borrowedValue is known to dominate this phi because if any reborrow exists then this utility returns false
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.
Fixed.
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
@@ -842,6 +859,85 @@ bool BorrowedValue::visitTransitiveLifetimeEndingUses( | |||
return true; | |||
} | |||
|
|||
bool BorrowedValue::visitTransitiveGuaranteedForwardingUses( |
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.
findTransitiveGuaranteed may reach the same instruction via multiple paths. We need to make sure we don't revisit the same instruction and have exponential path explosion
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.
Fixed.
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
return true; | ||
} | ||
|
||
bool BorrowedValue::visitTransitiveGuaranteedForwardingPhiUses( |
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.
Let's make sure that these utilities are only called on borrow-introducing values.
How can we make it clear that this utility is intentionally skipping over the first level of phi uses (that wasn't clear to me).
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.
I wasn't accurate when I said visitTransitiveGuaranteedForwardingPhis
these will only be called on borrow value introducers. I have moved it out of BorrowedValue
and added assert on values it can accept.
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.
Naming: visitTransitiveGuaranteedForwardingPhis
maybe something like
visitGuaranteedForwardingPhisForSSAValue
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.
Fixed.
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
} | ||
|
||
for (unsigned i = 0; i < guaranteedForwardingOps.size(); i++) { | ||
for (auto val : guaranteedForwardingOps[i]->getUser()->getResults()) { |
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.
If we make the structural SIL assumption that all GuaranteedForwarding results are borrowed values then we should check that somewhere
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.
I added a TODO. There are other places in SILOwnershipVerifier where we make the same assumption. I'll take it up as a follow on task.
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.
Maybe a simple assert can be added to verifyOperandOwnership? Such as canOpcodeForwardGuaranteedValues must be true for the user instruction...
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.
Done.
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
visitGuaranteedForwardingPhiBaseValuePair(phiValue, newBaseValue); | ||
|
||
collectGuaranteedForwardingPhis(phiValue, newBaseValue); | ||
collectGuaranteedForwardingPhis(newBaseValue, newBaseValue); |
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.
Explain: if an adjacent reborrow is found in the same block as the guaranteed phi, then set newBaseValue to the reborrow.
If we didn't find a reborrow, then don't call this
collectGuaranteedForwardingPhis(newBaseValue, newBaseValue);
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.
Fixed.
borrow.kind == BorrowedValueKind::LoadBorrow); | ||
// worklist stores (guaranteed forwarding phi, base value) pairs. | ||
// We need a SetVector to make sure we don't revisit the same pair again. | ||
SmallSetVector<std::tuple<PhiOperand, SILValue>, 4> worklist; |
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.
Do we really want to visit the same phi multiple times with a different base value each time?
I worry that could cause an infinite worklist.
Can we assert that a guaranteed phi always has the same borrow base?
Why is the worklist tracking operands? A phi should be identified by the its value.
Instead of std::tuple<PhiOperand, SILValue> worklist
Maybe it should be
ValueSet visitedPhis
`SmallVector<std::tuple<SILValue, SILValue> worklist // (guaranteedPhi, borrowBase)
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.
We can have different base values on different paths:
sil [ossa] @test_forwarded_separate_base_values : $@convention(thin) (@owned Wrapper1) -> () {
bb0(%0 : @owned $Wrapper1):
%outer = begin_borrow %0 : $Wrapper1
cond_br undef, bb1, bb2
bb1:
%unrelated = begin_borrow %0 : $Wrapper1
%ex1 = struct_extract %outer : $Wrapper1, #Wrapper1.val1
br bb3(%ex1 : $Wrapper2, %unrelated : $Wrapper1)
bb2:
%inner = begin_borrow %0 : $Wrapper1
%ex2 = struct_extract %inner : $Wrapper1, #Wrapper1.val1
br bb3(%ex2 : $Wrapper2, %inner : $Wrapper1)
bb3(%phi1 : @guaranteed $Wrapper2, %phi2 : @guaranteed $Wrapper1):
%f = function_ref @use_wrapper2 : $@convention(thin) (@guaranteed Wrapper2) -> ()
apply %f(%phi1) : $@convention(thin) (@guaranteed Wrapper2) -> ()
end_borrow %phi2 : $Wrapper1
end_borrow %outer : $Wrapper1
destroy_value %0 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
Here %phi1
has %phi2
as base value on one path and %outer
as base value in another.
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.
Comment long term, would like a "single-base" invariant!
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.
Instead of saying we want a "single-base" invariant. Please add the .sil above as a basic OSSA verification test case. Also please add the same test where the base is an owned value. That you can refer to the fact that a guaranteed phi can have multiple bases in a comment here, along with a stripped down version of the SIL. This comment and example should also appear at the top of the reborrow verifier!
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.
Done
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
return true; | ||
} | ||
|
||
bool BorrowedValue::visitExtendedTransitiveGuaranteedForwardingPhiUses( |
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.
The combination of BorrowedValue::visitTransitiveGuaranteedForwardingPhiUses
and BorrowedValue::visitExtendedTransitiveGuaranteedForwardingPhiUses
only makes sense as a single utility:
BorrowedValue::visitExtendedTransitiveGuaranteedForwardingPhis
that "finds all reachable guaranteed phis within a simple borrow scope"
We shouldn't need two levels of loops. We can have one loop that uses the same visited set and worklist.
We can assert that we can't have a guaranteed phi that is a direct use of borrowedValue.
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.
I just have one visitor now: visitTransitiveGuaranteedForwardingPhis
which finds all reachable guaranteed forwarding phis within a simple borrow scope
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.
One more thing... the first level of traversal can find more guaranteed forwarding phis (if we start from a guaranteed forwarding phi)
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.
Fixed.
lib/SIL/Utils/PrunedLiveness.cpp
Outdated
@@ -295,6 +296,13 @@ SimpleLiveRangeSummary PrunedLiveRange<LivenessWithDefs>::updateForDef(SILValue | |||
}); | |||
break; | |||
} | |||
case OperandOwnership::GuaranteedForwardingPhi: { | |||
SILArgument *phi = cast<BranchInst>(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.
I think the PhiOperand abstraction handles/hides all the BranchInst/DestBB stuff
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.
Fixed.
c99d928
to
68cf6a7
Compare
@swift-ci test |
1eac4cf
to
cfa3bd5
Compare
@swift-ci test |
@swift-ci test macOS Platform |
cfa3bd5
to
1f08b60
Compare
@swift-ci test |
@swift-ci test windows platform |
lib/SIL/Utils/OwnershipUtils.cpp
Outdated
// Iterate guaranteedForwardingOps and call the visitor on | ||
// GuaranteedForwardingPhi uses. | ||
for (unsigned i = 0; i < guaranteedForwardingOps.size(); i++) { | ||
for (auto val : guaranteedForwardingOps[i]->getUser()->getResults()) { |
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.
Is this loop necessary? All guaranteed forwarding op results were handled in the loop above. Why not call visitor directly on any phi uses in the above loop?
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.
@atrick
We need to process GuaranteedForwarding op results transitively, to find all GuaranteedForwardingPhi uses.
%b = begin_borrow ...
%ex1 = struct_extract %b ...
%ex2 = struct_extract %ex1 ...
....
br bbdest(%ex2)
We need the second loop to find %ex2
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.
Thanks for clarifying. I have fixed this.
1f08b60
to
ebb4deb
Compare
@swift-ci test |
@@ -919,7 +922,8 @@ inline OwnershipConstraint OperandOwnership::getOwnershipConstraint() { | |||
case OperandOwnership::ForwardingConsume: | |||
return {OwnershipKind::Owned, UseLifetimeConstraint::LifetimeEnding}; | |||
case OperandOwnership::InteriorPointer: | |||
case OperandOwnership::ForwardingBorrow: | |||
case OperandOwnership::GuaranteedForwarding: | |||
case OperandOwnership::GuaranteedForwardingPhi: | |||
return {OwnershipKind::Guaranteed, | |||
UseLifetimeConstraint::NonLifetimeEnding}; |
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.
@atrick I fixed GuaranteedForwardingPhi to NonLifetimeEnding!
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.
LGTM
@swift-ci test macOS Platform |
path for an optional type.
d5cfafb
to
283f15e
Compare
Resolved merge conflict... |
@swift-ci smoke test and merge |
We required borrow scopes to pass @guaranteed forwarding values as phi operands. With this change we eliminate unnecessary borrow scopes making it easier for optimizations to work with @guaranteed forwarding values.