Skip to content

Commit 2667df0

Browse files
authoredAug 11, 2023
Merge pull request #67760 from atrick/fix-closure-moveonly-arg
Fix compiler crashes with consuming and borrowing keywords.

15 files changed

+233
-169
lines changed
 

‎include/swift/SIL/ApplySite.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,11 @@ class ApplySite {
375375
///
376376
/// For full applies, this is equivalent to `getArgumentConvention`. But for
377377
/// a partial_apply, the argument ownership convention at the partial_apply
378-
/// instruction itself is different from the argument convention of the callee.
378+
/// instruction itself is different from the argument convention of the
379+
/// callee.
380+
///
379381
/// For details see the partial_apply documentation in SIL.rst.
380-
SILArgumentConvention getArgumentOperandConvention(const Operand &oper) const {
382+
SILArgumentConvention getCaptureConvention(const Operand &oper) const {
381383
SILArgumentConvention conv = getArgumentConvention(oper);
382384
auto *pai = dyn_cast<PartialApplyInst>(Inst);
383385
if (!pai)
@@ -388,6 +390,9 @@ class ApplySite {
388390
case SILArgumentConvention::Pack_Inout:
389391
return conv;
390392
case SILArgumentConvention::Direct_Owned:
393+
assert(!pai->isOnStack() &&
394+
"on-stack closures do not support owned arguments");
395+
return conv;
391396
case SILArgumentConvention::Direct_Unowned:
392397
case SILArgumentConvention::Direct_Guaranteed:
393398
return pai->isOnStack() ? SILArgumentConvention::Direct_Guaranteed

‎include/swift/SILOptimizer/Utils/InstOptUtils.h

+12-5
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,24 @@ void releasePartialApplyCapturedArg(
281281
SILParameterInfo paramInfo,
282282
InstModCallbacks callbacks = InstModCallbacks());
283283

284-
/// Insert destroys of captured arguments of partial_apply [stack].
284+
/// Insert destroys of captured arguments of partial_apply [stack]. \p builder
285+
/// indicates a position at which the closure's lifetime ends.
286+
///
287+
/// The \p getValueToDestroy callback allows the caller to handle some captured
288+
/// arguments specially. For example, ClosureLifetimeFixup generates borrow
289+
/// scopes for captured arguments; each getValueToDestroy callback then inserts
290+
/// the corresponding end_borrow and returns the owned operand of the borrow,
291+
/// which will then be destroyed as usual.
285292
void insertDestroyOfCapturedArguments(
286293
PartialApplyInst *pai, SILBuilder &builder,
287-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
288-
[](SILValue arg) -> bool { return true; },
294+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy =
295+
[](SILValue arg) -> SILValue { return arg; },
289296
SILLocation loc = RegularLocation::getAutoGeneratedLocation());
290297

291298
void insertDeallocOfCapturedArguments(PartialApplyInst *pai,
292299
DominanceInfo *domInfo,
293-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
294-
[](SILValue arg) -> bool { return true; });
300+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc =
301+
[](SILValue arg) -> SILValue { return arg; });
295302

296303
/// This iterator 'looks through' one level of builtin expect users exposing all
297304
/// users of the looked through builtin expect instruction i.e it presents a

‎lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
434434
ApplySite AS(&I);
435435
for (Operand &op : I.getAllOperands()) {
436436
if (AS.isArgumentOperand(op)) {
437-
setFuncOperandBits(state, op, AS.getArgumentOperandConvention(op),
437+
setFuncOperandBits(state, op, AS.getCaptureConvention(op),
438438
isa<TryApplyInst>(&I));
439439
}
440440
}
@@ -771,7 +771,7 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
771771
ApplySite AS(&I);
772772
for (Operand &op : I.getAllOperands()) {
773773
if (AS.isArgumentOperand(op))
774-
checkFuncArgument(bits, op, AS.getArgumentOperandConvention(op), &I);
774+
checkFuncArgument(bits, op, AS.getCaptureConvention(op), &I);
775775
}
776776
break;
777777
}

‎lib/SIL/Verifier/SILVerifier.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,11 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
19561956
unsigned appliedArgStartIdx =
19571957
substConv.getNumSILArguments() - PAI->getNumArguments();
19581958
for (auto p : llvm::enumerate(PAI->getArguments())) {
1959+
if (PAI->isOnStack()) {
1960+
require(
1961+
substConv.getSILArgumentConvention(appliedArgStartIdx + p.index()),
1962+
"on-stack closures do not support owned arguments");
1963+
}
19591964
requireSameType(
19601965
p.value()->getType(),
19611966
substConv.getSILArgumentType(appliedArgStartIdx + p.index(),

‎lib/SILOptimizer/Mandatory/CapturePromotion.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,9 @@ processPartialApplyInst(SILOptFunctionBuilder &funcBuilder,
15551555
builder.setInsertionPoint(std::next(SILBasicBlock::iterator(dsi)));
15561556
insertDestroyOfCapturedArguments(
15571557
newPAI, builder,
1558-
[&](SILValue arg) -> bool { return newCaptures.count(arg); });
1558+
[&](SILValue arg) -> SILValue {
1559+
return newCaptures.count(arg) ? arg : SILValue();
1560+
});
15591561
}
15601562
}
15611563
// Map the mark dependence arguments.

‎lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

+31-11
Original file line numberDiff line numberDiff line change
@@ -889,12 +889,28 @@ static SILValue tryRewriteToPartialApplyStack(
889889
destroy->dump();
890890
*/
891891
SILBuilderWithScope builder(std::next(destroy->getIterator()));
892-
insertDestroyOfCapturedArguments(newPA, builder,
893-
[&](SILValue arg) -> bool {
894-
// Don't need to destroy if we borrowed
895-
// in place .
896-
return !borrowedOriginals.count(arg);
897-
},
892+
// This getCapturedArg hack attempts to perfectly compensate for all the
893+
// other hacks involved in gathering new arguments above.
894+
auto getArgToDestroy = [&](SILValue argValue) -> SILValue {
895+
// A MoveOnlyWrapperToCopyableValueInst may produce a trivial value. Be
896+
// careful not to emit an extra destroy of the original.
897+
if (argValue->getType().isTrivial(argValue->getFunction()))
898+
return SILValue();
899+
900+
// We may have inserted a new begin_borrow->moveonlywrapper_to_copyvalue
901+
// when creating the new arguments. Now we need to end that borrow.
902+
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
903+
if (m->hasGuaranteedInitialKind())
904+
argValue = m->getOperand();
905+
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
906+
if (argBorrow) {
907+
argValue = argBorrow->getOperand();
908+
builder.createEndBorrow(newPA->getLoc(), argBorrow);
909+
}
910+
// Don't need to destroy if we borrowed in place .
911+
return borrowedOriginals.count(argValue) ? SILValue() : argValue;
912+
};
913+
insertDestroyOfCapturedArguments(newPA, builder, getArgToDestroy,
898914
newPA->getLoc());
899915
}
900916
/* DEBUG
@@ -919,13 +935,17 @@ static SILValue tryRewriteToPartialApplyStack(
919935
if (unreachableBlocks.count(newPA->getParent()))
920936
return closureOp;
921937

938+
auto getAddressToDealloc = [&](SILValue argAddress) -> SILValue {
939+
if (auto moveWrapper =
940+
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argAddress)) {
941+
argAddress = moveWrapper->getOperand();
942+
}
943+
// Don't need to destroy if we borrowed in place .
944+
return borrowedOriginals.count(argAddress) ? SILValue() : argAddress;
945+
};
922946
insertDeallocOfCapturedArguments(
923947
newPA, dominanceAnalysis->get(closureUser->getFunction()),
924-
[&](SILValue arg) -> bool {
925-
// Don't need to destroy if we borrowed
926-
// in place.
927-
return !borrowedOriginals.count(arg);
928-
});
948+
getAddressToDealloc);
929949

930950
return closureOp;
931951
}

‎lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableAddressesChecker.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ bool GatherLexicalLifetimeUseVisitor::visitUse(Operand *op,
13321332
return false;
13331333
}
13341334

1335-
if (fas.getArgumentOperandConvention(*op) ==
1335+
if (fas.getCaptureConvention(*op) ==
13361336
SILArgumentConvention::Indirect_InoutAliasable) {
13371337
// If we don't find the function, we can't handle this, so bail.
13381338
auto *func = fas.getCalleeFunction();

‎lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2378,7 +2378,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
23782378
// either.
23792379
if (auto *f = pas->getCalleeFunction()) {
23802380
if (f->hasSemanticsAttr(semantics::NO_MOVEONLY_DIAGNOSTICS)) {
2381-
if (ApplySite(pas).getArgumentOperandConvention(*op).isInoutConvention()) {
2381+
if (ApplySite(pas).getCaptureConvention(*op).isInoutConvention()) {
23822382
diagnosticEmitter.emitEarlierPassEmittedDiagnostic(markedValue);
23832383
return false;
23842384
}

‎lib/SILOptimizer/Mandatory/MoveOnlyUtils.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ bool noncopyable::memInstMustReinitialize(Operand *memOper) {
259259
case SILInstructionKind::TryApplyInst:
260260
case SILInstructionKind::ApplyInst: {
261261
FullApplySite applySite(memInst);
262-
return applySite.getArgumentOperandConvention(*memOper).isInoutConvention();
262+
return applySite.getCaptureConvention(*memOper).isInoutConvention();
263263
}
264264
case SILInstructionKind::StoreInst:
265265
return cast<StoreInst>(memInst)->getOwnershipQualifier() ==
@@ -296,7 +296,7 @@ bool noncopyable::memInstMustConsume(Operand *memOper) {
296296
case SILInstructionKind::TryApplyInst:
297297
case SILInstructionKind::ApplyInst: {
298298
FullApplySite applySite(memInst);
299-
return applySite.getArgumentOperandConvention(*memOper).isOwnedConvention();
299+
return applySite.getCaptureConvention(*memOper).isOwnedConvention();
300300
}
301301
case SILInstructionKind::PartialApplyInst: {
302302
// If we are on the stack or have an inout convention, we do not

‎lib/SILOptimizer/Utils/InstOptUtils.cpp

+22-26
Original file line numberDiff line numberDiff line change
@@ -902,13 +902,14 @@ void swift::emitDestroyOperation(SILBuilder &builder, SILLocation loc,
902902
callbacks.createdNewInst(u.get<ReleaseValueInst *>());
903903
}
904904

905-
// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is
906-
// printed with the convention of the closed over function upon it, all
907-
// non-inout arguments to a partial_apply are passed at +1. This includes
908-
// arguments that will eventually be passed as guaranteed or in_guaranteed to
909-
// the closed over function. This is because the partial apply is building up a
910-
// boxed aggregate to send off to the closed over function. Of course when you
911-
// call the function, the proper conventions will be used.
905+
// NOTE: The ownership of the partial_apply argument does not match the
906+
// convention of the closed over function. All non-inout arguments to a
907+
// partial_apply are passed at +1 for regular escaping closures and +0 for
908+
// closures that have been promoted to partial_apply [on_stack]. An escaping
909+
// partial apply stores each capture in an owned box, even for guaranteed and
910+
// in_guaranteed argument convention. A non-escaping/on-stack closure either
911+
// borrows its arguments or takes an inout_aliasable address. Non-escaping
912+
// closures do not support owned arguments.
912913
void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
913914
SILValue arg,
914915
SILParameterInfo paramInfo,
@@ -1479,7 +1480,7 @@ swift::findLocalApplySites(FunctionRefBaseInst *fri) {
14791480
/// Insert destroys of captured arguments of partial_apply [stack].
14801481
void swift::insertDestroyOfCapturedArguments(
14811482
PartialApplyInst *pai, SILBuilder &builder,
1482-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy,
1483+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy,
14831484
SILLocation origLoc) {
14841485
assert(pai->isOnStack());
14851486

@@ -1488,27 +1489,25 @@ void swift::insertDestroyOfCapturedArguments(
14881489
pai->getModule());
14891490
auto loc = CleanupLocation(origLoc);
14901491
for (auto &arg : pai->getArgumentOperands()) {
1491-
SILValue argValue = arg.get();
1492-
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
1493-
if (m->hasGuaranteedInitialKind())
1494-
argValue = m->getOperand();
1495-
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
1496-
if (argBorrow) {
1497-
argValue = argBorrow->getOperand();
1498-
builder.createEndBorrow(loc, argBorrow);
1499-
}
1500-
if (!shouldInsertDestroy(argValue))
1492+
SILValue argValue = getValueToDestroy(arg.get());
1493+
if (!argValue)
15011494
continue;
1495+
1496+
assert(!pai->getFunction()->hasOwnership()
1497+
|| (argValue->getOwnershipKind().isCompatibleWith(
1498+
OwnershipKind::Owned)));
1499+
15021500
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
15031501
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
15041502
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
15051503
releasePartialApplyCapturedArg(builder, loc, argValue, paramInfo);
15061504
}
15071505
}
15081506

1509-
void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
1510-
DominanceInfo *domInfo,
1511-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy)
1507+
void swift::insertDeallocOfCapturedArguments(
1508+
PartialApplyInst *pai,
1509+
DominanceInfo *domInfo,
1510+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc)
15121511
{
15131512
assert(pai->isOnStack());
15141513

@@ -1522,13 +1521,10 @@ void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
15221521
if (!paramInfo.isIndirectInGuaranteed())
15231522
continue;
15241523

1525-
SILValue argValue = arg.get();
1526-
if (!shouldInsertDestroy(argValue)) {
1524+
SILValue argValue = getAddressToDealloc(arg.get());
1525+
if (!argValue) {
15271526
continue;
15281527
}
1529-
if (auto moveWrapper =
1530-
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argValue))
1531-
argValue = moveWrapper->getOperand();
15321528

15331529
SmallVector<SILBasicBlock *, 4> boundary;
15341530
auto *asi = cast<AllocStackInst>(argValue);

0 commit comments

Comments
 (0)
Please sign in to comment.