Skip to content

[sil] Use FrozenMultiMap in PredictableMemOpts instead of implementing the data structure by hand. #29896

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions include/swift/SILOptimizer/Utils/ValueLifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ class ValueLifetimeAnalysis {
/// The lifetime frontier for the value. It is the list of instructions
/// following the last uses of the value. All the frontier instructions
/// end the value's lifetime.
typedef llvm::SmallVector<SILInstruction *, 4> Frontier;
using Frontier = SmallVector<SILInstruction *, 4>;

/// Constructor for the value \p Def with a specific set of users of Def's
/// users.
ValueLifetimeAnalysis(SILInstruction *def,
ArrayRef<SILInstruction *> userList)
: defValue(def), userSet(userList.begin(), userList.end()) {
/// Constructor for the value \p Def with a specific range of users.
///
/// We templatize over the RangeTy so that we can initialize
/// ValueLifetimeAnalysis with misc iterators including transform
/// iterators.
template <typename RangeTy>
ValueLifetimeAnalysis(SILInstruction *def, const RangeTy &userRange)
: defValue(def), userSet(userRange.begin(), userRange.end()) {
propagateLiveness();
}

Expand Down
54 changes: 15 additions & 39 deletions lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "PMOMemoryUseCollector.h"
#include "swift/Basic/BlotSetVector.h"
#include "swift/Basic/FrozenMultiMap.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/BranchPropagatedUser.h"
Expand Down Expand Up @@ -911,8 +912,10 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy,
return builder.emitCopyValueOperation(Loc, eltVal);
}

static SILInstruction *getNonPhiBlockIncomingValueDef(SILValue incomingValue,
CopyValueInst *phiCopy) {
static SILInstruction *
getNonPhiBlockIncomingValueDef(SILValue incomingValue,
SingleValueInstruction *phiCopy) {
assert(isa<CopyValueInst>(phiCopy));
auto *phiBlock = phiCopy->getParent();
if (phiBlock == incomingValue->getParentBlock()) {
return nullptr;
Expand Down Expand Up @@ -962,7 +965,7 @@ class PhiNodeCopyCleanupInserter {
/// visit our incoming values in visitation order and that within
/// their own values, also visit them in visitation order with
/// respect to each other.
SmallVector<std::pair<unsigned, CopyValueInst *>, 16> copiesToCleanup;
SmallFrozenMultiMap<unsigned, SingleValueInstruction *, 16> copiesToCleanup;

/// The lifetime frontier that we use to compute lifetime endpoints
/// when emitting cleanups.
Expand All @@ -976,7 +979,7 @@ class PhiNodeCopyCleanupInserter {
auto iter = incomingValues.insert(entry);
// If we did not succeed, then iter.first.second is the index of
// incoming value. Otherwise, it will be nextIndex.
copiesToCleanup.emplace_back(iter.first->second, copy);
copiesToCleanup.insert(iter.first->second, copy);
}

void emit(DeadEndBlocks &deadEndBlocks) &&;
Expand All @@ -999,36 +1002,15 @@ void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && {
// first phiNodeCleanupState for a specific phi, we process the phi
// then. This ensures that we always process the phis in insertion order as
// well.
SmallVector<unsigned, 32> copiesToCleanupIndicesSorted;
llvm::copy(indices(copiesToCleanup),
std::back_inserter(copiesToCleanupIndicesSorted));

stable_sort(copiesToCleanupIndicesSorted,
[&](unsigned lhsIndex, unsigned rhsIndex) {
unsigned lhs = copiesToCleanup[lhsIndex].first;
unsigned rhs = copiesToCleanup[rhsIndex].first;
return lhs < rhs;
});

for (auto ii = copiesToCleanupIndicesSorted.begin(),
ie = copiesToCleanupIndicesSorted.end();
ii != ie;) {
unsigned incomingValueIndex = copiesToCleanup[*ii].first;

// First find the end of the values for which ii does not equal baseValue.
auto rangeEnd = std::find_if_not(std::next(ii), ie, [&](unsigned index) {
return incomingValueIndex == copiesToCleanup[index].first;
});
copiesToCleanup.setFrozen();

SWIFT_DEFER {
// Once we have finished processing, set ii to rangeEnd. This ensures that
// the code below does not need to worry about updating the iterator.
ii = rangeEnd;
};
for (auto keyValue : copiesToCleanup.getRange()) {
unsigned incomingValueIndex = keyValue.first;
auto copies = keyValue.second;

SILValue incomingValue =
std::next(incomingValues.begin(), incomingValueIndex)->first;
CopyValueInst *phiCopy = copiesToCleanup[*ii].second;
SingleValueInstruction *phiCopy = copies.front();
auto *insertPt = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy);
auto loc = RegularLocation::getAutoGeneratedLocation();

Expand All @@ -1038,8 +1020,7 @@ void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && {
// copy and continue. This means that
// cleanupState.getNonPhiBlockIncomingValueDef() should always return a
// non-null value in the code below.
if (std::next(ii) == rangeEnd && isa<SILArgument>(incomingValue) &&
!insertPt) {
if (copies.size() == 1 && isa<SILArgument>(incomingValue) && !insertPt) {
SILBasicBlock *phiBlock = phiCopy->getParent();
SILBuilderWithScope builder(phiBlock->getTerminator());
builder.createDestroyValue(loc, incomingValue);
Expand All @@ -1048,17 +1029,12 @@ void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && {

// Otherwise, we know that we have for this incomingValue, multiple
// potential insert pts that we need to handle at the same time with our
// lifetime query. Gather up those uses.
SmallVector<SILInstruction *, 8> users;
transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users),
[&](unsigned index) { return copiesToCleanup[index].second; });

// Then lifetime extend our base over the copy_value.
// lifetime query. Lifetime extend our base over these copy_value uses.
assert(lifetimeFrontier.empty());
auto *def = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy);
assert(def && "Should never have a nullptr here since we handled all of "
"the single block cases earlier");
ValueLifetimeAnalysis analysis(def, users);
ValueLifetimeAnalysis analysis(def, copies);
bool foundCriticalEdges = !analysis.computeFrontier(
lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks);
(void)foundCriticalEdges;
Expand Down