Skip to content

Commit 1d95b83

Browse files
authored
Merge pull request swiftlang#30373 from gottesmm/pr-e0c4ada26f6653e4173eafc1b8c6c7c9c4fa4206
[semantic-arc-opts] Instead of tracking owned value introducers, track incoming values that are seen from owned value introducers.
2 parents f9129bf + faf4519 commit 1d95b83

File tree

1 file changed

+95
-65
lines changed

1 file changed

+95
-65
lines changed

lib/SILOptimizer/Transforms/SemanticARCOpts.cpp

Lines changed: 95 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,8 @@ LiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const {
447447
return HasConsumingUse_t::No;
448448
}
449449

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.
452452
if (!assumingAtFixPoint) {
453453
return HasConsumingUse_t::Yes;
454454
}
@@ -628,21 +628,35 @@ struct SemanticARCOptVisitor
628628
/// Are we assuming that we reached a fix point and are re-processing to
629629
/// prepare to use the phiToIncomingValueMultiMap.
630630
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;
643657

644658
using FrozenMultiMapRange =
645-
decltype(phiToIncomingValueMultiMap)::PairToSecondEltRange;
659+
decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange;
646660

647661
explicit SemanticARCOptVisitor(SILFunction &F) : F(F) {}
648662

@@ -800,88 +814,106 @@ static bool canEliminatePhi(
800814
SemanticARCOptVisitor::FrozenMultiMapRange optimizableIntroducerRange,
801815
ArrayRef<Operand *> incomingValueOperandList,
802816
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;
806817
for (Operand *incomingValueOperand : incomingValueOperandList) {
807-
SWIFT_DEFER { scratch.clear(); };
808-
809818
SILValue incomingValue = incomingValueOperand->get();
810819

811820
// Before we do anything, see if we have an incoming value with trivial
812821
// 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.
814823
if (incomingValue.getOwnershipKind() == ValueOwnershipKind::None) {
815824
continue;
816825
}
817826

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.
820829
//
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()) {
823835
return false;
824836
}
825837

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()) {
840851
return false;
841852
}
842853

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);
845856
}
846857

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
850872

851873
return true;
852874
}
853875

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+
854885
bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
855886
bool madeChange = false;
856887

857888
// First freeze our multi-map so we can use it for map queries. Also, setup a
858889
// 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(); };
861892

862893
// Now for each phi argument that we have in our multi-map...
863894
SmallVector<Operand *, 4> incomingValueOperandList;
864895
SmallVector<OwnedValueIntroducer, 4> ownedValueIntroducers;
865-
for (auto pair : phiToIncomingValueMultiMap.getRange()) {
896+
for (auto pair : joinedOwnedIntroducerToConsumedOperands.getRange()) {
866897
SWIFT_DEFER {
867898
incomingValueOperandList.clear();
868899
ownedValueIntroducers.clear();
869900
};
870901

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())) {
877908
continue;
878909
}
879910

880911
// Ok, we know that our phi argument /could/ be converted to guaranteed if
881912
// our incoming values are able to be converted to guaranteed. Now for each
882913
// incoming value, compute the incoming values ownership roots and see if
883914
// all of the ownership roots are in our owned incoming value array.
884-
if (!phi->getIncomingPhiOperands(incomingValueOperandList)) {
915+
if (!getIncomingJoinedLiveRangeOperands(joinedIntroducer,
916+
incomingValueOperandList)) {
885917
continue;
886918
}
887919

@@ -904,7 +936,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
904936
for (Operand *incomingValueOperand : incomingValueOperandList) {
905937
originalIncomingValues.push_back(incomingValueOperand->get());
906938
SILType type = incomingValueOperand->get()->getType();
907-
auto *undef = SILUndef::get(type, *phi->getFunction());
939+
auto *undef = SILUndef::get(type, F);
908940
incomingValueOperand->set(undef);
909941
}
910942

@@ -957,7 +989,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
957989
}
958990

959991
// Then convert the phi's live range to be guaranteed.
960-
std::move(phiLiveRange)
992+
std::move(joinedLiveRange)
961993
.convertArgToGuaranteed(getDeadEndBlocks(), lifetimeFrontier,
962994
getCallbacks());
963995

@@ -981,7 +1013,7 @@ bool SemanticARCOptVisitor::performPostPeepholeOwnedArgElimination() {
9811013

9821014
madeChange = true;
9831015
if (VerifyAfterTransform) {
984-
phi->getFunction()->verify();
1016+
F.verify();
9851017
}
9861018
}
9871019

@@ -1007,8 +1039,8 @@ bool SemanticARCOptVisitor::optimize() {
10071039

10081040
// Then re-run the worklist. We shouldn't modify anything since we are at a
10091041
// 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!
10121044
bool madeAdditionalChanges = processWorklist();
10131045
(void)madeAdditionalChanges;
10141046
assert(!madeAdditionalChanges && "Should be at the fixed point");
@@ -1177,7 +1209,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
11771209
// (e.x. storing into memory).
11781210
LiveRange lr(cvi);
11791211
auto hasUnknownConsumingUseState =
1180-
lr.hasUnknownConsumingUse(getPhiToIncomingValueMultiMap());
1212+
lr.hasUnknownConsumingUse(assumingAtFixedPoint);
11811213
if (hasUnknownConsumingUseState == LiveRange::HasConsumingUse_t::Yes) {
11821214
return false;
11831215
}
@@ -1283,10 +1315,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
12831315
// Otherwise, we know that our copy_value/destroy_values are all completely
12841316
// within the guaranteed value scope. So we /could/ optimize it. Now check if
12851317
// 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.
12901320
if (hasUnknownConsumingUseState ==
12911321
LiveRange::HasConsumingUse_t::YesButAllPhiArgs) {
12921322
auto *op = lr.getSingleUnknownConsumingUse();
@@ -1320,7 +1350,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
13201350

13211351
for (auto *succBlock : br->getSuccessorBlocks()) {
13221352
auto *arg = succBlock->getSILPhiArguments()[opNum];
1323-
phiToIncomingValueMultiMap.insert(arg, lr.getIntroducer());
1353+
joinedOwnedIntroducerToConsumedOperands.insert(arg, op);
13241354
}
13251355

13261356
return false;

0 commit comments

Comments
 (0)