11
11
// ===----------------------------------------------------------------------===//
12
12
13
13
#include " swift/SIL/OwnershipUtils.h"
14
+ #include " swift/Basic/DAGNodeWorklist.h"
14
15
#include " swift/Basic/Defer.h"
15
16
#include " swift/Basic/DAGNodeWorklist.h"
16
17
#include " swift/Basic/SmallPtrSetVector.h"
@@ -159,8 +160,7 @@ bool swift::canOpcodeForwardOwnedValues(Operand *use) {
159
160
//
160
161
// Skip over nested borrow scopes. Their scope-ending instructions are their use
161
162
// points. Transitively find all nested scope-ending instructions by looking
162
- // through nested reborrows. Nested reborrows are not use points and \p
163
- // visitReborrow is not called for them.
163
+ // through nested reborrows. Nested reborrows are not use points.
164
164
bool swift::findInnerTransitiveGuaranteedUses (
165
165
SILValue guaranteedValue, SmallVectorImpl<Operand *> *usePoints) {
166
166
@@ -175,10 +175,10 @@ bool swift::findInnerTransitiveGuaranteedUses(
175
175
//
176
176
// TODO: The worklist can be a simple vector without any a membership check if
177
177
// destructures are changed to be represented as reborrows. Currently a
178
- // destructure forwards multiple results! This means that usePoints could grow
179
- // exponentially without the membership check. It's fine to do this membership
180
- // check locally in this function (within a borrow scope) because it isn't
181
- // needed for the immediate uses, only the transitive uses.
178
+ // destructure forwards multiple results! This means that the worklist could
179
+ // grow exponentially without the membership check. It's fine to do this
180
+ // membership check locally in this function (within a borrow scope) because
181
+ // it isn't needed for the immediate uses, only the transitive uses.
182
182
DAGNodeWorklist<Operand *, 8 > worklist;
183
183
for (Operand *use : guaranteedValue->getUses ()) {
184
184
if (use->getOperandOwnership () != OperandOwnership::NonUse)
@@ -220,7 +220,8 @@ bool swift::findInnerTransitiveGuaranteedUses(
220
220
// If our base guaranteed value does not have any consuming uses (consider
221
221
// function arguments), we need to be sure to include interior pointer
222
222
// operands since we may not get a use from a end_scope instruction.
223
- if (!InteriorPointerOperand(use).findTransitiveUses(usePoints)) {
223
+ if (InteriorPointerOperand(use).findTransitiveUses(usePoints)
224
+ != AddressUseKind::NonEscaping) {
224
225
return false;
225
226
}
226
227
#endif
@@ -252,6 +253,7 @@ bool swift::findInnerTransitiveGuaranteedUses(
252
253
leafUse (endUse);
253
254
return true ;
254
255
});
256
+ break ;
255
257
}
256
258
}
257
259
return true ;
@@ -720,17 +722,35 @@ bool BorrowedValue::visitInteriorPointerOperandHelper(
720
722
return true ;
721
723
}
722
724
723
- bool swift::findTransitiveUsesForAddress (
724
- SILValue projectedAddress, SmallVectorImpl<Operand *> *foundUses,
725
- std::function<void (Operand *)> *onError) {
725
+ AddressUseKind
726
+ swift::findTransitiveUsesForAddress (SILValue projectedAddress,
727
+ SmallVectorImpl<Operand *> *foundUses,
728
+ std::function<void (Operand *)> *onError) {
729
+ // If the projectedAddress is dead, it is itself a leaf use. Since we don't
730
+ // have an operand for it, simply bail. Dead projectedAddress is unexpected.
731
+ //
732
+ // TODO: store_borrow is currently an InteriorPointer with no uses, so we end
733
+ // up bailing. It should be in a dependence scope instead. It's not clear why
734
+ // it produces an address at all.
735
+ if (projectedAddress->use_empty ())
736
+ return AddressUseKind::PointerEscape;
737
+
726
738
SmallVector<Operand *, 8 > worklist (projectedAddress->getUses ());
727
739
728
- bool foundError = false ;
740
+ AddressUseKind result = AddressUseKind::NonEscaping ;
729
741
730
742
auto leafUse = [foundUses](Operand *use) {
731
743
if (foundUses)
732
744
foundUses->push_back (use);
733
745
};
746
+ auto transitiveResultUses = [&](Operand *use) {
747
+ auto *svi = cast<SingleValueInstruction>(use->getUser ());
748
+ if (svi->use_empty ()) {
749
+ leafUse (use);
750
+ } else {
751
+ worklist.append (svi->use_begin (), svi->use_end ());
752
+ }
753
+ };
734
754
735
755
while (!worklist.empty ()) {
736
756
auto *op = worklist.pop_back_val ();
@@ -744,37 +764,44 @@ bool swift::findTransitiveUsesForAddress(
744
764
// loop that we didn't recognize the user.
745
765
auto *user = op->getUser ();
746
766
767
+ // TODO: Partial apply should be NonEscaping, but then we need to consider
768
+ // the apply to be a use point.
769
+ if (isa<PartialApplyInst>(user) || isa<AddressToPointerInst>(user)) {
770
+ result = meet (result, AddressUseKind::PointerEscape);
771
+ continue ;
772
+ }
747
773
// First, eliminate "end point uses" that we just need to check liveness at
748
774
// and do not need to check transitive uses of.
749
- if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) ||
750
- isIncidentalUse (user) || isa<StoreInst>(user) ||
751
- isa<PartialApplyInst>(user) || isa<DestroyAddrInst>(user) ||
752
- isa<AssignInst>(user) || isa<AddressToPointerInst>(user) ||
753
- isa<YieldInst>(user) || isa<LoadUnownedInst>(user) ||
754
- isa<StoreUnownedInst>(user) || isa<EndApplyInst>(user) ||
755
- isa<LoadWeakInst>(user) || isa<StoreWeakInst>(user) ||
756
- isa<AssignByWrapperInst>(user) || isa<BeginUnpairedAccessInst>(user) ||
757
- isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user) ||
758
- isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user) ||
759
- isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user) ||
760
- isa<IsUniqueInst>(user)) {
775
+ if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) || isIncidentalUse (user)
776
+ || isa<StoreInst>(user) || isa<DestroyAddrInst>(user)
777
+ || isa<AssignInst>(user) || isa<YieldInst>(user)
778
+ || isa<LoadUnownedInst>(user) || isa<StoreUnownedInst>(user)
779
+ || isa<EndApplyInst>(user) || isa<LoadWeakInst>(user)
780
+ || isa<StoreWeakInst>(user) || isa<AssignByWrapperInst>(user)
781
+ || isa<BeginUnpairedAccessInst>(user)
782
+ || isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user)
783
+ || isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user)
784
+ || isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user)
785
+ || isa<IsUniqueInst>(user)) {
761
786
leafUse (op);
762
787
continue ;
763
788
}
764
789
790
+ if (isa<UnconditionalCheckedCastAddrInst>(user)
791
+ || isa<MarkFunctionEscapeInst>(user)) {
792
+ assert (!user->hasResults ());
793
+ continue ;
794
+ }
795
+
765
796
// Then handle users that we need to look at transitive uses of.
766
797
if (Projection::isAddressProjection (user) ||
767
798
isa<ProjectBlockStorageInst>(user) ||
768
799
isa<OpenExistentialAddrInst>(user) ||
769
800
isa<InitExistentialAddrInst>(user) || isa<InitEnumDataAddrInst>(user) ||
770
801
isa<BeginAccessInst>(user) || isa<TailAddrInst>(user) ||
771
802
isa<IndexAddrInst>(user) || isa<StoreBorrowInst>(user) ||
772
- isa<UnconditionalCheckedCastAddrInst>(user) ||
773
- isa<UncheckedAddrCastInst>(user)
774
- || isa<MarkFunctionEscapeInst>(user)) {
775
- for (SILValue r : user->getResults ()) {
776
- llvm::copy (r->getUses (), std::back_inserter (worklist));
777
- }
803
+ isa<UncheckedAddrCastInst>(user)) {
804
+ transitiveResultUses (op);
778
805
continue ;
779
806
}
780
807
@@ -790,17 +817,26 @@ bool swift::findTransitiveUsesForAddress(
790
817
// If we have a load_borrow, add it's end scope to the liveness requirement.
791
818
if (auto *lbi = dyn_cast<LoadBorrowInst>(user)) {
792
819
if (foundUses) {
793
- transform (lbi->getEndBorrows (), std::back_inserter (*foundUses),
794
- [](EndBorrowInst *ebi) { return &ebi->getAllOperands ()[0 ]; });
820
+ for (Operand *use : lbi->getUses ()) {
821
+ if (use->endsLocalBorrowScope ()) {
822
+ leafUse (use);
823
+ }
824
+ }
795
825
}
796
826
continue ;
797
827
}
798
828
799
829
// TODO: Merge this into the full apply site code below.
800
830
if (auto *beginApply = dyn_cast<BeginApplyInst>(user)) {
801
831
if (foundUses) {
802
- llvm::copy (beginApply->getTokenResult ()->getUses (),
803
- std::back_inserter (*foundUses));
832
+ // TODO: the empty check should not be needed when dead begin_apply is
833
+ // disallowed.
834
+ if (beginApply->getTokenResult ()->use_empty ()) {
835
+ leafUse (op);
836
+ } else {
837
+ llvm::copy (beginApply->getTokenResult ()->getUses (),
838
+ std::back_inserter (*foundUses));
839
+ }
804
840
}
805
841
continue ;
806
842
}
@@ -818,20 +854,17 @@ bool swift::findTransitiveUsesForAddress(
818
854
}
819
855
820
856
// If we are the value use, look through it.
821
- llvm::copy (mdi-> getUses (), std::back_inserter (worklist) );
857
+ transitiveResultUses (op );
822
858
continue ;
823
859
}
824
860
825
861
// We were unable to recognize this user, so return true that we failed.
826
862
if (onError) {
827
863
(*onError)(op);
828
864
}
829
- foundError = true ;
865
+ result = AddressUseKind::Unknown ;
830
866
}
831
-
832
- // We were able to recognize all of the uses of the address, so return false
833
- // that we did not find any errors.
834
- return !foundError;
867
+ return result;
835
868
}
836
869
837
870
// ===----------------------------------------------------------------------===//
0 commit comments