Skip to content

Commit b847528

Browse files
authored
Merge pull request #71305 from jckarter/borrowing-switch-4
BorrowToDestructureUtils: handle borrowing switches.
2 parents 479ac68 + 067f1d3 commit b847528

File tree

4 files changed

+121
-47
lines changed

4 files changed

+121
-47
lines changed

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,23 @@ SubElementOffset::computeForValue(SILValue projectionDerivedFromRoot,
304304
// So our payload is always going to start at the current field number since
305305
// we are the left most child of our parent enum. So we just need to look
306306
// through to our parent enum.
307+
//
308+
// Enum projections can happen either directly via an unchecked instruction…
307309
if (auto *enumData =
308310
dyn_cast<UncheckedEnumDataInst>(projectionDerivedFromRoot)) {
309311
projectionDerivedFromRoot = enumData->getOperand();
310312
continue;
311313
}
314+
315+
// …or via the bb arg of a `switch_enum` successor.
316+
if (auto bbArg = dyn_cast<SILArgument>(projectionDerivedFromRoot)) {
317+
if (auto pred = bbArg->getParent()->getSinglePredecessorBlock()) {
318+
if (auto switchEnum = dyn_cast<SwitchEnumInst>(pred->getTerminator())) {
319+
projectionDerivedFromRoot = switchEnum->getOperand();
320+
continue;
321+
}
322+
}
323+
}
312324

313325
// If we do not know how to handle this case, just return None.
314326
//

lib/SILGen/SILGenPattern.cpp

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,8 @@ class PatternMatchEmission {
523523
const FailureHandler &failure);
524524

525525
void emitGuardBranch(SILLocation loc, Expr *guard,
526-
const FailureHandler &failure);
526+
const FailureHandler &failure,
527+
const ClauseRow &row, ArgArray args);
527528

528529
// Bind copyable variable bindings as independent variables.
529530
void bindIrrefutablePatterns(const ClauseRow &row, ArgArray args,
@@ -1183,25 +1184,21 @@ void PatternMatchEmission::emitWildcardDispatch(ClauseMatrix &clauses,
11831184

11841185
if (auto ownership = getNoncopyableOwnership()) {
11851186
// A noncopyable pattern match always happens over a borrow first.
1186-
// If there is a guard expression, then we also have to match pattern
1187-
// variables as borrows while executing the guard. This ensures
1188-
// the guard can't consume or modify the value without us committing to this
1189-
// case branch. If the final pattern match is only borrowing as well,
1187+
// If the final pattern match is only borrowing as well,
11901188
// we can bind the variables immediately here too.
1191-
if (hasGuard || *ownership <= ValueOwnership::Shared) {
1189+
if (*ownership <= ValueOwnership::Shared) {
11921190
bindIrrefutableBorrows(clauses[row], args);
11931191
}
11941192

11951193
if (hasGuard) {
1196-
this->emitGuardBranch(guardExpr, guardExpr, failure);
1194+
// The guard will bind borrows locally if necessary.
1195+
this->emitGuardBranch(guardExpr, guardExpr, failure,
1196+
clauses[row], args);
11971197
}
11981198

11991199
if (*ownership > ValueOwnership::Shared) {
1200-
// Unbind the variables and unborrow the subject so we can consume it
1201-
// later.
12021200
unbindAndEndBorrows(clauses[row], args);
12031201
}
1204-
12051202
} else {
12061203
// Bind the rest of the patterns.
12071204
// For noncopyable bindings, this will bind them as borrows initially if there
@@ -1211,7 +1208,8 @@ void PatternMatchEmission::emitWildcardDispatch(ClauseMatrix &clauses,
12111208

12121209
// Emit the guard branch, if it exists.
12131210
if (guardExpr) {
1214-
this->emitGuardBranch(guardExpr, guardExpr, failure);
1211+
this->emitGuardBranch(guardExpr, guardExpr, failure,
1212+
clauses[row], args);
12151213
}
12161214
}
12171215

@@ -1244,7 +1242,8 @@ bindRefutablePatterns(const ClauseRow &row, ArgArray args,
12441242
FullExpr scope(SGF.Cleanups, CleanupLocation(pattern));
12451243
bindVariable(pattern, exprPattern->getMatchVar(), args[i],
12461244
/*isForSuccess*/ false, /* hasMultipleItems */ false);
1247-
emitGuardBranch(pattern, exprPattern->getMatchExpr(), failure);
1245+
emitGuardBranch(pattern, exprPattern->getMatchExpr(), failure,
1246+
row, args);
12481247
break;
12491248
}
12501249
default:
@@ -1391,21 +1390,39 @@ void PatternMatchEmission::bindBorrow(Pattern *pattern, VarDecl *var,
13911390
ConsumableManagedValue value) {
13921391
assert(value.getFinalConsumption() == CastConsumptionKind::BorrowAlways);
13931392

1394-
SGF.VarLocs[var] = SILGenFunction::VarLoc::get(
1395-
value.asBorrowedOperand2(SGF, pattern).getValue());
1393+
auto bindValue = value.asBorrowedOperand2(SGF, pattern).getFinalManagedValue();
1394+
if (bindValue.getType().isMoveOnly()) {
1395+
if (bindValue.getType().isObject()) {
1396+
// Create a notional copy for the borrow checker to use.
1397+
bindValue = bindValue.copy(SGF, pattern);
1398+
}
1399+
bindValue = SGF.B.createMarkUnresolvedNonCopyableValueInst(pattern, bindValue,
1400+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
1401+
}
1402+
SGF.VarLocs[var] = SILGenFunction::VarLoc::get(bindValue.getValue());
13961403
}
13971404

13981405
/// Evaluate a guard expression and, if it returns false, branch to
13991406
/// the given destination.
14001407
void PatternMatchEmission::emitGuardBranch(SILLocation loc, Expr *guard,
1401-
const FailureHandler &failure) {
1408+
const FailureHandler &failure,
1409+
const ClauseRow &row, ArgArray args){
14021410
SILBasicBlock *falseBB = SGF.B.splitBlockForFallthrough();
14031411
SILBasicBlock *trueBB = SGF.B.splitBlockForFallthrough();
14041412

14051413
// Emit the match test.
14061414
SILValue testBool;
14071415
{
14081416
FullExpr scope(SGF.Cleanups, CleanupLocation(guard));
1417+
1418+
// If the final pattern match is destructive, then set up borrow bindings
1419+
// to evaluate the guard expression without allowing it to destruct the
1420+
// subject yet.
1421+
if (auto ownership = getNoncopyableOwnership()) {
1422+
if (*ownership > ValueOwnership::Shared) {
1423+
bindIrrefutableBorrows(row, args);
1424+
}
1425+
}
14091426
testBool = SGF.emitRValueAsSingleValue(guard).getUnmanagedValue();
14101427
}
14111428

@@ -2720,33 +2737,28 @@ void PatternMatchEmission::emitDestructiveCaseBlocks() {
27202737
CleanupState::PersistentlyActive);
27212738
}
27222739

2723-
{
2724-
// Create a scope to break down the subject value.
2725-
Scope caseScope(SGF, pattern);
2726-
2727-
// Clone the original subject's cleanup state so that it will be reliably
2728-
// consumed in this scope, while leaving the original for other case
2729-
// blocks to re-consume.
2730-
ManagedValue subject = SGF.emitManagedRValueWithCleanup(
2731-
NoncopyableConsumableValue.forward(SGF));
2732-
2733-
// TODO: handle fallthroughs and multiple cases bindings
2734-
// In those cases we'd need to forward bindings through the shared case
2735-
// destination blocks.
2736-
assert(!stmt->hasFallthroughDest()
2737-
&& stmt->getCaseLabelItems().size() == 1);
2738-
2739-
// Bind variables from the pattern.
2740-
if (stmt->hasCaseBodyVariables()) {
2741-
ConsumingPatternBindingVisitor(*this, stmt)
2742-
.visit(pattern, subject);
2743-
}
2744-
2745-
// By this point, whatever parts of the value we bound are forwarded into
2746-
// variables, so we can pop the scope and destroy the remainder before
2747-
// entering the case body (TODO: or common block).
2748-
}
2740+
// Create a scope to break down the subject value.
2741+
Scope caseScope(SGF, pattern);
27492742

2743+
// Clone the original subject's cleanup state so that it will be reliably
2744+
// consumed in this scope, while leaving the original for other case
2745+
// blocks to re-consume.
2746+
ManagedValue subject = SGF.emitManagedRValueWithCleanup(
2747+
NoncopyableConsumableValue.forward(SGF));
2748+
2749+
// TODO: handle fallthroughs and multiple cases bindings
2750+
// In those cases we'd need to forward bindings through the shared case
2751+
// destination blocks.
2752+
assert(!stmt->hasFallthroughDest()
2753+
&& stmt->getCaseLabelItems().size() == 1);
2754+
2755+
// Bind variables from the pattern.
2756+
if (stmt->hasCaseBodyVariables()) {
2757+
ConsumingPatternBindingVisitor(*this, stmt)
2758+
.visit(pattern, subject);
2759+
}
2760+
2761+
// Emit the case body.
27502762
emitCaseBody(stmt);
27512763
}
27522764
}

lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureUtils.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,29 @@ bool Implementation::gatherUses(SILValue value) {
372372
}
373373

374374
case OperandOwnership::GuaranteedForwarding: {
375+
// Always treat switches as full liveness uses of the enum being switched
376+
// since the control flow is significant, and we can't destructure through
377+
// the switch dispatch. If the final pattern match ends up destructuring
378+
// the value, then SILGen emits that as a separate access.
379+
if (auto switchEnum = dyn_cast<SwitchEnumInst>(nextUse->getUser())) {
380+
auto leafRange = TypeTreeLeafTypeRange::get(switchEnum->getOperand(),
381+
getRootValue());
382+
if (!leafRange) {
383+
LLVM_DEBUG(llvm::dbgs() << " Failed to compute leaf range?!\n");
384+
return false;
385+
}
386+
387+
LLVM_DEBUG(llvm::dbgs() << " Found non lifetime ending use!\n");
388+
blocksToUses.insert(nextUse->getParentBlock(),
389+
{nextUse,
390+
{liveness.getNumSubElements(), *leafRange,
391+
false /*is lifetime ending*/}});
392+
liveness.updateForUse(nextUse->getUser(), *leafRange,
393+
false /*is lifetime ending*/);
394+
instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse);
395+
continue;
396+
}
397+
375398
// Look through guaranteed forwarding if we have at least one non-trivial
376399
// value. If we have all non-trivial values, treat this as a liveness use.
377400
SmallVector<SILValue, 8> forwardedValues;
@@ -1044,6 +1067,23 @@ static void insertEndBorrowsForNonConsumingUse(Operand *op,
10441067
borrow);
10451068
return true;
10461069
});
1070+
} else if (auto swi = dyn_cast<SwitchEnumInst>(op->getUser())) {
1071+
LLVM_DEBUG(llvm::dbgs() << " -- Ending borrow for switch:\n"
1072+
" ";
1073+
swi->print(llvm::dbgs()));
1074+
// End the borrow where the original borrow of the subject was ended.
1075+
// TODO: handle if the switch isn't directly on a borrow?
1076+
auto beginBorrow = cast<BeginBorrowInst>(swi->getOperand());
1077+
BorrowingOperand(&beginBorrow->getOperandRef())
1078+
.visitScopeEndingUses([&](Operand *endScope) -> bool {
1079+
auto *endScopeInst = endScope->getUser();
1080+
LLVM_DEBUG(llvm::dbgs() << " ";
1081+
endScopeInst->print(llvm::dbgs()));
1082+
SILBuilderWithScope endBuilder(endScopeInst);
1083+
endBuilder.createEndBorrow(getSafeLoc(endScopeInst),
1084+
borrow);
1085+
return true;
1086+
});
10471087
} else {
10481088
auto *nextInst = op->getUser()->getNextInstruction();
10491089
LLVM_DEBUG(llvm::dbgs() << " -- Ending borrow after momentary use at: ";
@@ -1521,9 +1561,17 @@ static bool gatherBorrows(SILValue rootValue,
15211561
static bool
15221562
gatherSwitchEnum(SILValue value,
15231563
SmallVectorImpl<SwitchEnumInst *> &switchEnumWorklist) {
1564+
auto *fn = value->getFunction();
1565+
auto &C = fn->getASTContext();
1566+
// TODO: For borrowing switches, we want to leave the switch as a borrowing
1567+
// operation. This phase of the pass should become moot once borrowing
1568+
// switch is on by default.
1569+
if (C.LangOpts.hasFeature(Feature::BorrowingSwitch)) {
1570+
return true;
1571+
}
1572+
15241573
LLVM_DEBUG(llvm::dbgs() << "Gathering switch enums for value: " << *value);
15251574

1526-
auto *fn = value->getFunction();
15271575
StackList<Operand *> useWorklist(fn);
15281576
for (auto *use : value->getUses()) {
15291577
useWorklist.push_back(use);
@@ -1639,8 +1687,10 @@ gatherSwitchEnum(SILValue value,
16391687
//===----------------------------------------------------------------------===//
16401688

16411689
bool BorrowToDestructureTransform::transform() {
1642-
LLVM_DEBUG(llvm::dbgs() << "Performing Borrow To Destructure Tranform!\n");
16431690
auto *fn = mmci->getFunction();
1691+
LLVM_DEBUG(llvm::dbgs() << "Performing Borrow To Destructure Transform!\n";
1692+
fn->print(llvm::dbgs()));
1693+
auto &C = fn->getASTContext();
16441694
StackList<BeginBorrowInst *> borrowWorklist(mmci->getFunction());
16451695

16461696
// If we failed to gather borrows due to the transform not understanding part

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,10 @@ void MoveOnlyObjectCheckerPImpl::check(DominanceInfo *domTree,
488488

489489
// Handle:
490490
//
491-
// bb0(%0 : @guaranteed $Type):
492-
// %1 = copy_value %0
493-
// %2 = mark_unresolved_non_copyable_value [no_consume_or_assign] %1
494-
if (auto *arg = dyn_cast<SILFunctionArgument>(i->getOperand(0))) {
491+
// bb(%arg : @guaranteed $Type):
492+
// %copy = copy_value %arg
493+
// %mark = mark_unresolved_non_copyable_value [no_consume_or_assign] %copy
494+
if (auto *arg = dyn_cast<SILArgument>(i->getOperand(0))) {
495495
if (arg->getOwnershipKind() == OwnershipKind::Guaranteed) {
496496
for (auto *use : markedInst->getConsumingUses()) {
497497
destroys.push_back(cast<DestroyValueInst>(use->getUser()));

0 commit comments

Comments
 (0)