@@ -88,6 +88,70 @@ struct DenseMapInfo<swift::PartitionPrimitives::Region> {
88
88
89
89
namespace swift {
90
90
91
+ struct TransferringOperand {
92
+ using ValueType = llvm::PointerIntPair<Operand *, 1 >;
93
+ ValueType value;
94
+
95
+ TransferringOperand () : value() {}
96
+ TransferringOperand (Operand *op, bool isClosureCaptured)
97
+ : value(op, isClosureCaptured) {}
98
+ explicit TransferringOperand (Operand *op) : value(op, false ) {}
99
+ TransferringOperand (ValueType newValue) : value(newValue) {}
100
+
101
+ operator bool () const { return bool (value.getPointer ()); }
102
+
103
+ Operand *getOperand () const { return value.getPointer (); }
104
+
105
+ bool isClosureCaptured () const { return value.getInt (); }
106
+
107
+ SILInstruction *getUser () const { return getOperand ()->getUser (); }
108
+ };
109
+
110
+ } // namespace swift
111
+
112
+ namespace llvm {
113
+
114
+ template <>
115
+ struct PointerLikeTypeTraits <swift::TransferringOperand> {
116
+ using TransferringOperand = swift::TransferringOperand;
117
+
118
+ static inline void *getAsVoidPointer (TransferringOperand ptr) {
119
+ return PointerLikeTypeTraits<
120
+ TransferringOperand::ValueType>::getAsVoidPointer (ptr.value );
121
+ }
122
+ static inline TransferringOperand getFromVoidPointer (void *ptr) {
123
+ return {PointerLikeTypeTraits<
124
+ TransferringOperand::ValueType>::getFromVoidPointer (ptr)};
125
+ }
126
+
127
+ static constexpr int NumLowBitsAvailable = PointerLikeTypeTraits<
128
+ TransferringOperand::ValueType>::NumLowBitsAvailable;
129
+ };
130
+
131
+ template <>
132
+ struct DenseMapInfo <swift::TransferringOperand> {
133
+ using TransferringOperand = swift::TransferringOperand;
134
+ using ParentInfo = DenseMapInfo<TransferringOperand::ValueType>;
135
+
136
+ static TransferringOperand getEmptyKey () {
137
+ return TransferringOperand (ParentInfo::getEmptyKey ());
138
+ }
139
+ static TransferringOperand getTombstoneKey () {
140
+ return TransferringOperand (ParentInfo::getTombstoneKey ());
141
+ }
142
+
143
+ static unsigned getHashValue (TransferringOperand operand) {
144
+ return ParentInfo::getHashValue (operand.value );
145
+ }
146
+ static bool isEqual (TransferringOperand LHS, TransferringOperand RHS) {
147
+ return ParentInfo::isEqual (LHS.value , RHS.value );
148
+ }
149
+ };
150
+
151
+ } // namespace llvm
152
+
153
+ namespace swift {
154
+
91
155
// / PartitionOpKind represents the different kinds of PartitionOps that
92
156
// / SILInstructions can be translated to
93
157
enum class PartitionOpKind : uint8_t {
@@ -142,7 +206,7 @@ class PartitionOp {
142
206
: opKind(opKind), opArgs({arg1}), source(sourceOperand) {
143
207
assert (((opKind != PartitionOpKind::Transfer &&
144
208
opKind != PartitionOpKind::UndoTransfer) ||
145
- sourceOperand) &&
209
+ bool ( sourceOperand) ) &&
146
210
" Transfer needs a sourceInst" );
147
211
}
148
212
@@ -304,7 +368,7 @@ class Partition {
304
368
// / multi map here. The implication of this is that when we are performing
305
369
// / dataflow we use a union operation to combine CFG elements and just take
306
370
// / the first instruction that we see.
307
- llvm::SmallDenseMap<Region, Operand * , 2 > regionToTransferredOpMap;
371
+ llvm::SmallDenseMap<Region, TransferringOperand , 2 > regionToTransferredOpMap;
308
372
309
373
public:
310
374
Partition () : elementToRegionMap({}), canonical(true ) {}
@@ -358,12 +422,12 @@ class Partition {
358
422
359
423
// / Mark val as transferred. Returns true if we inserted \p
360
424
// / transferOperand. We return false otherwise.
361
- bool markTransferred (Element val, Operand *transferOperand ) {
425
+ bool markTransferred (Element val, TransferringOperand transferredOperand ) {
362
426
// First see if our val is tracked. If it is not tracked, insert it and mark
363
427
// its new region as transferred.
364
428
if (!isTracked (val)) {
365
429
elementToRegionMap.insert_or_assign (val, fresh_label);
366
- regionToTransferredOpMap.insert ({fresh_label, transferOperand });
430
+ regionToTransferredOpMap.insert ({fresh_label, transferredOperand });
367
431
fresh_label = Region (fresh_label + 1 );
368
432
canonical = false ;
369
433
return true ;
@@ -373,7 +437,7 @@ class Partition {
373
437
auto iter1 = elementToRegionMap.find (val);
374
438
assert (iter1 != elementToRegionMap.end ());
375
439
auto iter2 =
376
- regionToTransferredOpMap.try_emplace (iter1->second , transferOperand );
440
+ regionToTransferredOpMap.try_emplace (iter1->second , transferredOperand );
377
441
return iter2.second ;
378
442
}
379
443
@@ -532,13 +596,30 @@ class Partition {
532
596
533
597
os << " [" ;
534
598
for (auto [regionNo, elementNumbers] : multimap.getRange ()) {
535
- bool isTransferred = regionToTransferredOpMap.count (regionNo);
536
- os << (isTransferred ? " {" : " (" );
599
+ auto iter = regionToTransferredOpMap.find (regionNo);
600
+ bool isTransferred = iter != regionToTransferredOpMap.end ();
601
+ bool isClosureCaptured =
602
+ isTransferred ? iter->getSecond ().isClosureCaptured () : false ;
603
+
604
+ if (isTransferred) {
605
+ os << ' {' ;
606
+ if (isClosureCaptured)
607
+ os << ' *' ;
608
+ } else {
609
+ os << ' (' ;
610
+ }
611
+
537
612
int j = 0 ;
538
613
for (Element i : elementNumbers) {
539
614
os << (j++ ? " " : " " ) << i;
540
615
}
541
- os << (isTransferred ? " }" : " )" );
616
+ if (isTransferred) {
617
+ if (isClosureCaptured)
618
+ os << ' *' ;
619
+ os << ' }' ;
620
+ } else {
621
+ os << ' )' ;
622
+ }
542
623
}
543
624
os << " ]\n " ;
544
625
}
@@ -552,13 +633,13 @@ class Partition {
552
633
553
634
// / Return the instruction that transferred \p val's region or nullptr
554
635
// / otherwise.
555
- Operand * getTransferred (Element val) const {
636
+ std::optional<TransferringOperand> getTransferred (Element val) const {
556
637
auto iter = elementToRegionMap.find (val);
557
638
if (iter == elementToRegionMap.end ())
558
- return nullptr ;
639
+ return std::nullopt ;
559
640
auto iter2 = regionToTransferredOpMap.find (iter->second );
560
641
if (iter2 == regionToTransferredOpMap.end ())
561
- return nullptr ;
642
+ return std::nullopt ;
562
643
return iter2->second ;
563
644
}
564
645
@@ -642,7 +723,7 @@ class Partition {
642
723
//
643
724
// TODO: If we just used an array for this, we could just rewrite and
644
725
// re-sort and not have to deal with potential allocations.
645
- llvm::SmallDenseMap<Region, Operand * , 2 > oldMap =
726
+ llvm::SmallDenseMap<Region, TransferringOperand , 2 > oldMap =
646
727
std::move (regionToTransferredOpMap);
647
728
for (auto &[oldReg, op] : oldMap) {
648
729
auto iter = oldRegionToRelabeledMap.find (oldReg);
@@ -676,17 +757,19 @@ class Partition {
676
757
horizontalUpdate (elementToRegionMap, snd, fstRegion);
677
758
auto iter = regionToTransferredOpMap.find (sndRegion);
678
759
if (iter != regionToTransferredOpMap.end ()) {
679
- regionToTransferredOpMap. try_emplace (fstRegion, iter->second ) ;
760
+ auto operand = iter->second ;
680
761
regionToTransferredOpMap.erase (iter);
762
+ regionToTransferredOpMap.try_emplace (fstRegion, operand);
681
763
}
682
764
} else {
683
765
result = sndRegion;
684
766
685
767
horizontalUpdate (elementToRegionMap, fst, sndRegion);
686
768
auto iter = regionToTransferredOpMap.find (fstRegion);
687
769
if (iter != regionToTransferredOpMap.end ()) {
688
- regionToTransferredOpMap. try_emplace (sndRegion, iter->second ) ;
770
+ auto operand = iter->second ;
689
771
regionToTransferredOpMap.erase (iter);
772
+ regionToTransferredOpMap.try_emplace (sndRegion, operand);
690
773
}
691
774
}
692
775
@@ -751,8 +834,8 @@ struct PartitionOpEvaluator {
751
834
// / 3. The operand of the instruction that originally transferred the
752
835
// / region. Can be used to get the immediate value transferred or the
753
836
// / transferring instruction.
754
- std::function<void (const PartitionOp &, Element, Operand *)> failureCallback =
755
- nullptr ;
837
+ std::function<void (const PartitionOp &, Element, TransferringOperand)>
838
+ failureCallback = nullptr ;
756
839
757
840
// / A list of elements that cannot be transferred. Whenever we transfer, we
758
841
// / check this list to see if we are transferring the element and then call
@@ -771,11 +854,21 @@ struct PartitionOpEvaluator {
771
854
// / transferred.
772
855
std::function<bool (Element)> isActorDerivedCallback = nullptr ;
773
856
857
+ // / Check if the representative value of \p elt is closure captured at \p
858
+ // / op.
859
+ // /
860
+ // / NOTE: We actually just use the user of \p op in our callbacks. The reason
861
+ // / why we do not just pass in that SILInstruction is that then we would need
862
+ // / to access the instruction in the evaluator which creates a problem when
863
+ // / since the operand we pass in is a dummy operand.
864
+ std::function<bool (Element elt, Operand *op)> isClosureCapturedCallback =
865
+ nullptr ;
866
+
774
867
PartitionOpEvaluator (Partition &p) : p(p) {}
775
868
776
869
// / A wrapper around the failure callback that checks if it is nullptr.
777
870
void handleFailure (const PartitionOp &op, Element elt,
778
- Operand * transferringOp) const {
871
+ TransferringOperand transferringOp) const {
779
872
if (!failureCallback)
780
873
return ;
781
874
failureCallback (op, elt, transferringOp);
@@ -797,6 +890,14 @@ struct PartitionOpEvaluator {
797
890
return bool (isActorDerivedCallback) && isActorDerivedCallback (elt);
798
891
}
799
892
893
+ // / A wraper around isClosureCapturedCallback that returns false if
894
+ // / isClosureCapturedCallback is nullptr and otherwise returns
895
+ // / isClosureCapturedCallback.
896
+ bool isClosureCaptured (Element elt, Operand *op) const {
897
+ return bool (isClosureCapturedCallback) &&
898
+ isClosureCapturedCallback (elt, op);
899
+ }
900
+
800
901
// / Apply \p op to the partition op.
801
902
void apply (const PartitionOp &op) const {
802
903
if (emitLog) {
@@ -821,8 +922,8 @@ struct PartitionOpEvaluator {
821
922
" Assign PartitionOp's source argument should be already tracked" );
822
923
// If we are using a region that was transferred as our assignment source
823
924
// value... emit an error.
824
- if (auto *transferringInst = p.getTransferred (op.getOpArgs ()[1 ]))
825
- handleFailure (op, op.getOpArgs ()[1 ], transferringInst );
925
+ if (auto transferredOperand = p.getTransferred (op.getOpArgs ()[1 ]))
926
+ handleFailure (op, op.getOpArgs ()[1 ], *transferredOperand );
826
927
827
928
p.elementToRegionMap .insert_or_assign (
828
929
op.getOpArgs ()[0 ], p.elementToRegionMap .at (op.getOpArgs ()[1 ]));
@@ -863,17 +964,22 @@ struct PartitionOpEvaluator {
863
964
if (isActorDerived (op.getOpArgs ()[0 ]))
864
965
return handleTransferNonTransferrable (op, op.getOpArgs ()[0 ]);
865
966
967
+ // While we are checking for actor derived, also check if our value or any
968
+ // value in our region is closure captured and propagate that bit in our
969
+ // transferred inst.
970
+ bool isClosureCapturedElt =
971
+ isClosureCaptured (op.getOpArgs ()[0 ], op.getSourceOp ());
972
+
866
973
Region elementRegion = p.elementToRegionMap .at (op.getOpArgs ()[0 ]);
867
- if (llvm::any_of (p.elementToRegionMap ,
868
- [&](const std::pair<Element, Region> &pair) -> bool {
869
- if (pair.second != elementRegion)
870
- return false ;
871
- return isActorDerived (pair.first );
872
- }))
873
- return handleTransferNonTransferrable (op, op.getOpArgs ()[0 ]);
974
+ for (const auto &pair : p.elementToRegionMap ) {
975
+ if (pair.second == elementRegion && isActorDerived (pair.first ))
976
+ return handleTransferNonTransferrable (op, op.getOpArgs ()[0 ]);
977
+ isClosureCapturedElt |= isClosureCaptured (pair.first , op.getSourceOp ());
978
+ }
874
979
875
980
// Mark op.getOpArgs()[0] as transferred.
876
- p.markTransferred (op.getOpArgs ()[0 ], op.getSourceOp ());
981
+ p.markTransferred (op.getOpArgs ()[0 ],
982
+ {op.getSourceOp (), isClosureCapturedElt});
877
983
return ;
878
984
}
879
985
case PartitionOpKind::UndoTransfer: {
@@ -894,10 +1000,10 @@ struct PartitionOpEvaluator {
894
1000
" Merge PartitionOp's arguments should already be tracked" );
895
1001
896
1002
// if attempting to merge a transferred region, handle the failure
897
- if (auto *transferringInst = p.getTransferred (op.getOpArgs ()[0 ]))
898
- handleFailure (op, op.getOpArgs ()[0 ], transferringInst );
899
- if (auto *transferringInst = p.getTransferred (op.getOpArgs ()[1 ]))
900
- handleFailure (op, op.getOpArgs ()[1 ], transferringInst );
1003
+ if (auto transferringOp = p.getTransferred (op.getOpArgs ()[0 ]))
1004
+ handleFailure (op, op.getOpArgs ()[0 ], *transferringOp );
1005
+ if (auto transferringOp = p.getTransferred (op.getOpArgs ()[1 ]))
1006
+ handleFailure (op, op.getOpArgs ()[1 ], *transferringOp );
901
1007
902
1008
p.merge (op.getOpArgs ()[0 ], op.getOpArgs ()[1 ]);
903
1009
return ;
@@ -906,8 +1012,8 @@ struct PartitionOpEvaluator {
906
1012
" Require PartitionOp should be passed 1 argument" );
907
1013
assert (p.elementToRegionMap .count (op.getOpArgs ()[0 ]) &&
908
1014
" Require PartitionOp's argument should already be tracked" );
909
- if (auto *transferringInst = p.getTransferred (op.getOpArgs ()[0 ]))
910
- handleFailure (op, op.getOpArgs ()[0 ], transferringInst );
1015
+ if (auto transferringOp = p.getTransferred (op.getOpArgs ()[0 ]))
1016
+ handleFailure (op, op.getOpArgs ()[0 ], *transferringOp );
911
1017
return ;
912
1018
}
913
1019
0 commit comments