Skip to content

Commit 97b68be

Browse files
authored
Merge pull request #63530 from gottesmm/moveonly-enum-destructure
[move-only] Add support for switch_enum in borrow2destructure
2 parents 43c5824 + d56e169 commit 97b68be

17 files changed

+1986
-606
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 356 additions & 23 deletions
Large diffs are not rendered by default.

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 114 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "swift/SIL/BasicBlockDatastructures.h"
2020
#include "swift/SIL/BasicBlockUtils.h"
2121
#include "swift/SIL/OwnershipUtils.h"
22-
#include "swift/SIL/PrunedLiveness.h"
2322
#include "swift/SIL/SILBuilder.h"
2423
#include "swift/SIL/SILInstruction.h"
2524
#include "swift/SIL/ScopedAddressUtils.h"
@@ -445,14 +444,111 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
445444
}
446445
}
447446

447+
//===----------------------------------------------------------------------===//
448+
// MARK: FieldSensitivePrunedLiveBlocks
449+
//===----------------------------------------------------------------------===//
450+
451+
void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness(
452+
SILBasicBlock *userBB, unsigned bitNo) {
453+
// If, we are visiting this block, then it is not already LiveOut. Mark it
454+
// LiveWithin to indicate a liveness boundary within the block.
455+
markBlockLive(userBB, bitNo, LiveWithin);
456+
457+
BasicBlockWorklist worklist(userBB->getFunction());
458+
worklist.push(userBB);
459+
460+
while (auto *block = worklist.pop()) {
461+
// The popped `bb` is live; now mark all its predecessors LiveOut.
462+
//
463+
// Traversal terminates at any previously visited block, including the
464+
// blocks initialized as definition blocks.
465+
for (auto *predBlock : block->getPredecessorBlocks()) {
466+
switch (getBlockLiveness(predBlock, bitNo)) {
467+
case Dead:
468+
worklist.pushIfNotVisited(predBlock);
469+
LLVM_FALLTHROUGH;
470+
case LiveWithin:
471+
markBlockLive(predBlock, bitNo, LiveOut);
472+
break;
473+
case LiveOut:
474+
break;
475+
}
476+
}
477+
}
478+
}
479+
480+
/// Update the current def's liveness based on one specific use instruction.
481+
///
482+
/// Return the updated liveness of the \p use block (LiveOut or LiveWithin).
483+
///
484+
/// Terminators are not live out of the block.
485+
void FieldSensitivePrunedLiveBlocks::updateForUse(
486+
SILInstruction *user, unsigned startBitNo, unsigned endBitNo,
487+
SmallVectorImpl<IsLive> &resultingLivenessInfo) {
488+
assert(isInitialized());
489+
resultingLivenessInfo.clear();
490+
491+
SWIFT_ASSERT_ONLY(seenUse = true);
492+
493+
auto *bb = user->getParent();
494+
getBlockLiveness(bb, startBitNo, endBitNo, resultingLivenessInfo);
495+
496+
for (auto pair : llvm::enumerate(resultingLivenessInfo)) {
497+
unsigned index = pair.index();
498+
unsigned specificBitNo = startBitNo + index;
499+
switch (pair.value()) {
500+
case LiveOut:
501+
case LiveWithin:
502+
continue;
503+
case Dead: {
504+
// This use block has not yet been marked live. Mark it and its
505+
// predecessor blocks live.
506+
computeScalarUseBlockLiveness(bb, specificBitNo);
507+
resultingLivenessInfo.push_back(getBlockLiveness(bb, specificBitNo));
508+
continue;
509+
}
510+
}
511+
llvm_unreachable("covered switch");
512+
}
513+
}
514+
515+
llvm::StringRef
516+
FieldSensitivePrunedLiveBlocks::getStringRef(IsLive isLive) const {
517+
switch (isLive) {
518+
case Dead:
519+
return "Dead";
520+
case LiveWithin:
521+
return "LiveWithin";
522+
case LiveOut:
523+
return "LiveOut";
524+
}
525+
}
526+
527+
void FieldSensitivePrunedLiveBlocks::print(llvm::raw_ostream &OS) const {
528+
if (!discoveredBlocks) {
529+
OS << "No deterministic live block list\n";
530+
return;
531+
}
532+
SmallVector<IsLive, 8> isLive;
533+
for (auto *block : *discoveredBlocks) {
534+
block->printAsOperand(OS);
535+
OS << ": ";
536+
for (unsigned i : range(getNumBitsToTrack()))
537+
OS << getStringRef(this->getBlockLiveness(block, i)) << ", ";
538+
OS << "\n";
539+
}
540+
}
541+
542+
void FieldSensitivePrunedLiveBlocks::dump() const { print(llvm::dbgs()); }
543+
448544
//===----------------------------------------------------------------------===//
449545
// MARK: FieldSensitiveLiveness
450546
//===----------------------------------------------------------------------===//
451547

452548
void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user,
453549
TypeTreeLeafTypeRange range,
454550
bool lifetimeEnding) {
455-
SmallVector<PrunedLiveBlocks::IsLive, 8> resultingLiveness;
551+
SmallVector<FieldSensitivePrunedLiveBlocks::IsLive, 8> resultingLiveness;
456552
liveBlocks.updateForUse(user, range.startEltOffset, range.endEltOffset,
457553
resultingLiveness);
458554

@@ -479,7 +575,7 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
479575
return true;
480576
}
481577

482-
using IsLive = PrunedLiveBlocks::IsLive;
578+
using IsLive = FieldSensitivePrunedLiveBlocks::IsLive;
483579

484580
auto *block = inst->getParent();
485581

@@ -491,12 +587,12 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
491587
LLVM_DEBUG(llvm::dbgs() << " Visiting bit: " << bit << '\n');
492588
bool isLive = false;
493589
switch (pair.value()) {
494-
case PrunedLiveBlocks::Dead:
590+
case FieldSensitivePrunedLiveBlocks::Dead:
495591
LLVM_DEBUG(llvm::dbgs() << " Dead... continuing!\n");
496592
// We are only not within the boundary if all of our bits are dead. We
497593
// track this via allDeadBits. So, just continue.
498594
continue;
499-
case PrunedLiveBlocks::LiveOut:
595+
case FieldSensitivePrunedLiveBlocks::LiveOut:
500596
// If we are LiveOut and are not a def block, then we know that we are
501597
// within the boundary for this bit. We consider ourselves to be within
502598
// the boundary if /any/ of our bits are within the boundary. So return
@@ -513,7 +609,7 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
513609
LLVM_DEBUG(llvm::dbgs()
514610
<< " LiveOut, but a def block... searching block!\n");
515611
[[clang::fallthrough]];
516-
case PrunedLiveBlocks::LiveWithin:
612+
case FieldSensitivePrunedLiveBlocks::LiveWithin:
517613
bool shouldContinue = false;
518614
if (!isLive)
519615
LLVM_DEBUG(llvm::dbgs() << " LiveWithin... searching block!\n");
@@ -576,13 +672,13 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
576672
return false;
577673
}
578674

579-
static StringRef getStringRef(PrunedLiveBlocks::IsLive isLive) {
675+
static StringRef getStringRef(FieldSensitivePrunedLiveBlocks::IsLive isLive) {
580676
switch (isLive) {
581-
case PrunedLiveBlocks::Dead:
677+
case FieldSensitivePrunedLiveBlocks::Dead:
582678
return "Dead";
583-
case PrunedLiveBlocks::LiveWithin:
679+
case FieldSensitivePrunedLiveBlocks::LiveWithin:
584680
return "LiveWithin";
585-
case PrunedLiveBlocks::LiveOut:
681+
case FieldSensitivePrunedLiveBlocks::LiveOut:
586682
return "LiveOut";
587683
}
588684
}
@@ -594,7 +690,7 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
594690

595691
LLVM_DEBUG(llvm::dbgs() << "Liveness Boundary Compuation!\n");
596692

597-
using IsLive = PrunedLiveBlocks::IsLive;
693+
using IsLive = FieldSensitivePrunedLiveBlocks::IsLive;
598694
SmallVector<IsLive, 8> isLiveTmp;
599695
for (SILBasicBlock *block : getDiscoveredBlocks()) {
600696
SWIFT_DEFER { isLiveTmp.clear(); };
@@ -610,9 +706,10 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
610706
LLVM_DEBUG(llvm::dbgs() << "Bit: " << index << ". Liveness: "
611707
<< getStringRef(pair.value()) << '\n');
612708
switch (pair.value()) {
613-
case PrunedLiveBlocks::LiveOut:
709+
case FieldSensitivePrunedLiveBlocks::LiveOut:
614710
for (SILBasicBlock *succBB : block->getSuccessors()) {
615-
if (getBlockLiveness(succBB, index) == PrunedLiveBlocks::Dead) {
711+
if (getBlockLiveness(succBB, index) ==
712+
FieldSensitivePrunedLiveBlocks::Dead) {
616713
LLVM_DEBUG(llvm::dbgs() << "Marking succBB as boundary edge: bb"
617714
<< succBB->getDebugID() << '\n');
618715
boundary.getBoundaryEdgeBits(succBB).set(index);
@@ -622,13 +719,13 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
622719
boundary);
623720
foundAnyNonDead = true;
624721
break;
625-
case PrunedLiveBlocks::LiveWithin: {
722+
case FieldSensitivePrunedLiveBlocks::LiveWithin: {
626723
asImpl().findBoundariesInBlock(block, index, /*isLiveOut*/ false,
627724
boundary);
628725
foundAnyNonDead = true;
629726
break;
630727
}
631-
case PrunedLiveBlocks::Dead:
728+
case FieldSensitivePrunedLiveBlocks::Dead:
632729
// We do not assert here like in the normal pruned liveness
633730
// implementation since we can have dead on some bits and liveness along
634731
// others.
@@ -682,7 +779,7 @@ void findBoundaryInNonDefBlock(SILBasicBlock *block, unsigned bitNo,
682779
FieldSensitivePrunedLivenessBoundary &boundary,
683780
const FieldSensitivePrunedLiveness &liveness) {
684781
assert(liveness.getBlockLiveness(block, bitNo) ==
685-
PrunedLiveBlocks::LiveWithin);
782+
FieldSensitivePrunedLiveBlocks::LiveWithin);
686783

687784
LLVM_DEBUG(llvm::dbgs() << "Looking for boundary in non-def block\n");
688785
for (SILInstruction &inst : llvm::reverse(*block)) {
@@ -895,7 +992,7 @@ void FieldSensitiveMultiDefPrunedLiveRange::findBoundariesInBlock(
895992
if (llvm::all_of(block->getPredecessorBlocks(),
896993
[&](SILBasicBlock *predBlock) -> bool {
897994
return getBlockLiveness(predBlock, bitNo) ==
898-
PrunedLiveBlocks::IsLive::LiveOut;
995+
FieldSensitivePrunedLiveBlocks::IsLive::LiveOut;
899996
})) {
900997
boundary.getBoundaryEdgeBits(block).set(bitNo);
901998
}

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
#include "swift/SIL/BasicBlockDatastructures.h"
1717
#include "swift/SIL/BasicBlockUtils.h"
1818
#include "swift/SIL/OwnershipUtils.h"
19-
#include "swift/SIL/ScopedAddressUtils.h"
2019
#include "swift/SIL/SILInstruction.h"
20+
#include "swift/SIL/SILValue.h"
21+
#include "swift/SIL/ScopedAddressUtils.h"
2122

2223
using namespace swift;
2324

lib/SILGen/SILGenPattern.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,14 +2843,28 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
28432843

28442844
// Inline constructor for subject.
28452845
auto subject = ([&]() -> ConsumableManagedValue {
2846-
// If we have a move only value, ensure plus one and convert it. Switches
2847-
// always consume move only values.
2846+
// If we have a noImplicitCopy value, ensure plus one and convert
2847+
// it. Switches always consume move only values.
2848+
//
2849+
// NOTE: We purposely do not do this for pure move only types since for them
2850+
// we emit everything at +0 and then run the BorrowToDestructure transform
2851+
// upon them. The reason that we do this is that internally to
2852+
// SILGenPattern, we always attempt to move from +1 -> +0 meaning that even
2853+
// if we start at +1, we will go back to +0 given enough patterns to go
2854+
// through. It is simpler to just let SILGenPattern do what it already wants
2855+
// to do, rather than fight it or try to resusitate the "fake owned borrow"
2856+
// path that we still use for address only types (and that we want to delete
2857+
// once we have opaque values).
28482858
if (subjectMV.getType().isMoveOnly() && subjectMV.getType().isObject()) {
28492859
if (subjectMV.getType().isMoveOnlyWrapped()) {
28502860
subjectMV = B.createOwnedMoveOnlyWrapperToCopyableValue(
28512861
S, subjectMV.ensurePlusOne(*this, S));
28522862
} else {
2853-
subjectMV = B.createMoveValue(S, subjectMV.ensurePlusOne(*this, S));
2863+
// If we have a pure move only type and it is owned, borrow it so that
2864+
// BorrowToDestructure can handle it.
2865+
if (subjectMV.getOwnershipKind() == OwnershipKind::Owned) {
2866+
subjectMV = subjectMV.borrow(*this, S);
2867+
}
28542868
}
28552869
}
28562870

0 commit comments

Comments
 (0)