@@ -447,8 +447,8 @@ LiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const {
447
447
return HasConsumingUse_t::No;
448
448
}
449
449
450
- // Ok, we do have some unknown consuming uses. If we aren't asked to
451
- // update phiToIncomingValueMultiMap, then just return true quickly .
450
+ // Ok, we do have some unknown consuming uses. If we aren't assuming we are at
451
+ // the fixed point yet, just bail .
452
452
if (!assumingAtFixPoint) {
453
453
return HasConsumingUse_t::Yes;
454
454
}
@@ -628,21 +628,35 @@ struct SemanticARCOptVisitor
628
628
// / Are we assuming that we reached a fix point and are re-processing to
629
629
// / prepare to use the phiToIncomingValueMultiMap.
630
630
bool assumingAtFixedPoint = false ;
631
- FrozenMultiMap<SILPhiArgument *, OwnedValueIntroducer>
632
- phiToIncomingValueMultiMap;
633
-
634
- // / Returns the phiToIncomingValueMultiMap if we are re-processing our
635
- // / worklist after fixed point to initialize our phi to incoming value
636
- // / multi-map. Otherwise returns nullptr.
637
- FrozenMultiMap<SILPhiArgument *, OwnedValueIntroducer> *
638
- getPhiToIncomingValueMultiMap () {
639
- if (assumingAtFixedPoint)
640
- return &phiToIncomingValueMultiMap;
641
- return nullptr ;
642
- }
631
+
632
+ // / A map from a value that acts as a "joined owned introducer" in the def-use
633
+ // / graph.
634
+ // /
635
+ // / A "joined owned introducer" is a value with owned ownership whose
636
+ // / ownership is derived from multiple non-trivial owned operands of a related
637
+ // / instruction. Some examples are phi arguments, tuples, structs. Naturally,
638
+ // / all of these instructions must be non-unary instructions and only have
639
+ // / this property if they have multiple operands that are non-trivial.
640
+ // /
641
+ // / In such a case, we can not just treat them like normal forwarding concepts
642
+ // / since we can only eliminate optimize such a value if we are able to reason
643
+ // / about all of its operands together jointly. This is not amenable to a
644
+ // / small peephole analysis.
645
+ // /
646
+ // / Instead, as we perform the peephole analysis, using the multimap, we map
647
+ // / each joined owned value introducer to the set of its @owned operands that
648
+ // / we thought we could convert to guaranteed only if we could do the same to
649
+ // / the joined owned value introducer. Then once we finish performing
650
+ // / peepholes, we iterate through the map and see if any of our joined phi
651
+ // / ranges had all of their operand's marked with this property by iterating
652
+ // / over the multimap. Since we are dealing with owned values and we know that
653
+ // / our LiveRange can not see through joined live ranges, we know that we
654
+ // / should only be able to have a single owned value introducer for each
655
+ // / consumed operand.
656
+ FrozenMultiMap<SILValue, Operand *> joinedOwnedIntroducerToConsumedOperands;
643
657
644
658
using FrozenMultiMapRange =
645
- decltype (phiToIncomingValueMultiMap )::PairToSecondEltRange;
659
+ decltype (joinedOwnedIntroducerToConsumedOperands )::PairToSecondEltRange;
646
660
647
661
explicit SemanticARCOptVisitor (SILFunction &F) : F(F) {}
648
662
@@ -800,88 +814,106 @@ static bool canEliminatePhi(
800
814
SemanticARCOptVisitor::FrozenMultiMapRange optimizableIntroducerRange,
801
815
ArrayRef<Operand *> incomingValueOperandList,
802
816
SmallVectorImpl<OwnedValueIntroducer> &ownedValueIntroducerAccumulator) {
803
- // A set that we use to ensure we only add introducers to the accumulator
804
- // once.
805
- SmallVector<OwnedValueIntroducer, 16 > scratch;
806
817
for (Operand *incomingValueOperand : incomingValueOperandList) {
807
- SWIFT_DEFER { scratch.clear (); };
808
-
809
818
SILValue incomingValue = incomingValueOperand->get ();
810
819
811
820
// Before we do anything, see if we have an incoming value with trivial
812
821
// ownership. This can occur in the case where we are working with enums due
813
- // to trivial non-payloaded cases.
822
+ // to trivial non-payloaded cases. Skip that.
814
823
if (incomingValue.getOwnershipKind () == ValueOwnershipKind::None) {
815
824
continue ;
816
825
}
817
826
818
- // Now that we know it is an owned value, check for introducers of the owned
819
- // value which are the copies that we may be able to eliminate .
827
+ // Then see if this is an introducer that we actually saw as able to be
828
+ // optimized if we could flip this joined live range .
820
829
//
821
- // If we can not find all of the owned value's introducers, bail.
822
- if (!getAllOwnedValueIntroducers (incomingValue, scratch)) {
830
+ // NOTE: If this linear search is too slow, we can change the multimap to
831
+ // sort the mapped to list by pointer instead of insertion order. In such a
832
+ // case, we could then bisect.
833
+ if (llvm::find (optimizableIntroducerRange, incomingValueOperand) ==
834
+ optimizableIntroducerRange.end ()) {
823
835
return false ;
824
836
}
825
837
826
- // Then make sure that all of our owned value introducers are able to be
827
- // converted to guaranteed and that we found it to have a LiveRange that we
828
- // could have eliminated /if/ we were to get rid of this phi.
829
- if (!llvm::all_of (scratch, [&](const OwnedValueIntroducer &introducer) {
830
- if (!introducer.isConvertableToGuaranteed ()) {
831
- return false ;
832
- }
833
- // If this linear search is too slow, we can change the
834
- // multimap to sort the mapped to list by pointer
835
- // instead of insertion order. In such a case, we could
836
- // then bisect.
837
- auto iter = llvm::find (optimizableIntroducerRange, introducer);
838
- return iter != optimizableIntroducerRange.end ();
839
- })) {
838
+ // Now that we know it is an owned value that we saw before, check for
839
+ // introducers of the owned value which are the copies that we may be able
840
+ // to eliminate. Since we do not look through joined live ranges, we must
841
+ // only have a single introducer. So look for that one and if not, bail.
842
+ auto singleIntroducer = getSingleOwnedValueIntroducer (incomingValue);
843
+ if (!singleIntroducer.hasValue ()) {
844
+ return false ;
845
+ }
846
+
847
+ // Then make sure that our owned value introducer is able to be converted to
848
+ // guaranteed and that we found it to have a LiveRange that we could have
849
+ // eliminated /if/ we were to get rid of this phi.
850
+ if (!singleIntroducer->isConvertableToGuaranteed ()) {
840
851
return false ;
841
852
}
842
853
843
- // Otherwise, append all introducers from scratch into our result array.
844
- llvm::copy (scratch, std::back_inserter ( ownedValueIntroducerAccumulator) );
854
+ // Otherwise, add the introducer to our result array.
855
+ ownedValueIntroducerAccumulator. push_back (*singleIntroducer );
845
856
}
846
857
847
- // Now that we are done, perform a sort unique on our result array so that we
848
- // on return have a unique set of values.
849
- sortUnique (ownedValueIntroducerAccumulator);
858
+ #ifndef NDEBUG
859
+ // Other parts of the pass ensure that we only add values to the list if their
860
+ // owned value introducer is not used by multiple live ranges. That being
861
+ // said, lets assert that.
862
+ {
863
+ SmallVector<OwnedValueIntroducer, 32 > uniqueCheck;
864
+ llvm::copy (ownedValueIntroducerAccumulator,
865
+ std::back_inserter (uniqueCheck));
866
+ sortUnique (uniqueCheck);
867
+ assert (
868
+ uniqueCheck.size () == ownedValueIntroducerAccumulator.size () &&
869
+ " multiple joined live range operands are from the same live range?!" );
870
+ }
871
+ #endif
850
872
851
873
return true ;
852
874
}
853
875
876
+ static bool getIncomingJoinedLiveRangeOperands (
877
+ SILValue joinedLiveRange, SmallVectorImpl<Operand *> &resultingOperands) {
878
+ if (auto *phi = dyn_cast<SILPhiArgument>(joinedLiveRange)) {
879
+ return phi->getIncomingPhiOperands (resultingOperands);
880
+ }
881
+
882
+ llvm_unreachable (" Unhandled joined live range?!" );
883
+ }
884
+
854
885
bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination () {
855
886
bool madeChange = false ;
856
887
857
888
// First freeze our multi-map so we can use it for map queries. Also, setup a
858
889
// defer of the reset so we do not forget to reset the map when we are done.
859
- phiToIncomingValueMultiMap .setFrozen ();
860
- SWIFT_DEFER { phiToIncomingValueMultiMap .reset (); };
890
+ joinedOwnedIntroducerToConsumedOperands .setFrozen ();
891
+ SWIFT_DEFER { joinedOwnedIntroducerToConsumedOperands .reset (); };
861
892
862
893
// Now for each phi argument that we have in our multi-map...
863
894
SmallVector<Operand *, 4 > incomingValueOperandList;
864
895
SmallVector<OwnedValueIntroducer, 4 > ownedValueIntroducers;
865
- for (auto pair : phiToIncomingValueMultiMap .getRange ()) {
896
+ for (auto pair : joinedOwnedIntroducerToConsumedOperands .getRange ()) {
866
897
SWIFT_DEFER {
867
898
incomingValueOperandList.clear ();
868
899
ownedValueIntroducers.clear ();
869
900
};
870
901
871
- // First compute the LiveRange for our phi argument . For simplicity, we only
872
- // handle cases now where our phi argument does not have any phi unknown
873
- // consumers .
874
- SILPhiArgument *phi = pair.first ;
875
- LiveRange phiLiveRange (phi );
876
- if (bool (phiLiveRange .hasUnknownConsumingUse ())) {
902
+ // First compute the LiveRange for ownershipPhi value . For simplicity, we
903
+ // only handle cases now where the result does not have any additional
904
+ // ownershipPhi uses .
905
+ SILValue joinedIntroducer = pair.first ;
906
+ LiveRange joinedLiveRange (joinedIntroducer );
907
+ if (bool (joinedLiveRange .hasUnknownConsumingUse ())) {
877
908
continue ;
878
909
}
879
910
880
911
// Ok, we know that our phi argument /could/ be converted to guaranteed if
881
912
// our incoming values are able to be converted to guaranteed. Now for each
882
913
// incoming value, compute the incoming values ownership roots and see if
883
914
// all of the ownership roots are in our owned incoming value array.
884
- if (!phi->getIncomingPhiOperands (incomingValueOperandList)) {
915
+ if (!getIncomingJoinedLiveRangeOperands (joinedIntroducer,
916
+ incomingValueOperandList)) {
885
917
continue ;
886
918
}
887
919
@@ -904,7 +936,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
904
936
for (Operand *incomingValueOperand : incomingValueOperandList) {
905
937
originalIncomingValues.push_back (incomingValueOperand->get ());
906
938
SILType type = incomingValueOperand->get ()->getType ();
907
- auto *undef = SILUndef::get (type, *phi-> getFunction () );
939
+ auto *undef = SILUndef::get (type, F );
908
940
incomingValueOperand->set (undef);
909
941
}
910
942
@@ -957,7 +989,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
957
989
}
958
990
959
991
// Then convert the phi's live range to be guaranteed.
960
- std::move (phiLiveRange )
992
+ std::move (joinedLiveRange )
961
993
.convertArgToGuaranteed (getDeadEndBlocks (), lifetimeFrontier,
962
994
getCallbacks ());
963
995
@@ -981,7 +1013,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
981
1013
982
1014
madeChange = true ;
983
1015
if (VerifyAfterTransform) {
984
- phi-> getFunction ()-> verify ();
1016
+ F. verify ();
985
1017
}
986
1018
}
987
1019
@@ -1007,8 +1039,8 @@ bool SemanticARCOptVisitor::optimize() {
1007
1039
1008
1040
// Then re-run the worklist. We shouldn't modify anything since we are at a
1009
1041
// fixed point and are just using this to seed the
1010
- // phiToIncomingValueMultiMap after we have finished changing things. If we
1011
- // did change something, we did something weird, so assert!
1042
+ // joinedOwnedIntroducerToConsumedOperands after we have finished changing
1043
+ // things. If we did change something, we did something weird, so assert!
1012
1044
bool madeAdditionalChanges = processWorklist ();
1013
1045
(void )madeAdditionalChanges;
1014
1046
assert (!madeAdditionalChanges && " Should be at the fixed point" );
@@ -1177,7 +1209,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
1177
1209
// (e.x. storing into memory).
1178
1210
LiveRange lr (cvi);
1179
1211
auto hasUnknownConsumingUseState =
1180
- lr.hasUnknownConsumingUse (getPhiToIncomingValueMultiMap () );
1212
+ lr.hasUnknownConsumingUse (assumingAtFixedPoint );
1181
1213
if (hasUnknownConsumingUseState == LiveRange::HasConsumingUse_t::Yes) {
1182
1214
return false ;
1183
1215
}
@@ -1283,10 +1315,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
1283
1315
// Otherwise, we know that our copy_value/destroy_values are all completely
1284
1316
// within the guaranteed value scope. So we /could/ optimize it. Now check if
1285
1317
// we were truly dead or if we are dead if we can eliminate phi arg uses. If
1286
- // we need to handle the phi arg uses, we bail. When we checked if the value
1287
- // was consumed, the hasConsumedUse code updated phiToIncomingValueMultiMap
1288
- // for us before returning its prognosis. After we reach a fixed point, we
1289
- // will try to eliminate this value then.
1318
+ // we need to handle the phi arg uses, we bail. After we reach a fixed point,
1319
+ // we will try to eliminate this value then.
1290
1320
if (hasUnknownConsumingUseState ==
1291
1321
LiveRange::HasConsumingUse_t::YesButAllPhiArgs) {
1292
1322
auto *op = lr.getSingleUnknownConsumingUse ();
@@ -1320,7 +1350,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
1320
1350
1321
1351
for (auto *succBlock : br->getSuccessorBlocks ()) {
1322
1352
auto *arg = succBlock->getSILPhiArguments ()[opNum];
1323
- phiToIncomingValueMultiMap .insert (arg, lr. getIntroducer () );
1353
+ joinedOwnedIntroducerToConsumedOperands .insert (arg, op );
1324
1354
}
1325
1355
1326
1356
return false ;
0 commit comments