Skip to content

Commit 6fd7762

Browse files
authored
Merge pull request swiftlang#63798 from jckarter/owned-nonescaping-closure-asserts
[SIL] Add assertions to check assumptions about owned noescape closures.
2 parents 7012632 + 1081d1e commit 6fd7762

File tree

4 files changed

+62
-40
lines changed

4 files changed

+62
-40
lines changed

include/swift/SIL/SILCloner.h

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,23 +1962,10 @@ void SILCloner<ImplClass>::visitDestroyValueInst(DestroyValueInst *Inst) {
19621962
if (fnTy->isTrivialNoEscape()) {
19631963
// Destroying the partial_apply [stack] becomes the stack deallocation
19641964
// of the context.
1965-
// Look through mark_dependence and other wrapper instructions.
1966-
SILValue deallocOperand = Inst->getOperand();
1967-
while (true) {
1968-
if (auto mdi = dyn_cast<MarkDependenceInst>(deallocOperand)) {
1969-
deallocOperand = mdi->getValue();
1970-
} else if (isa<ConvertEscapeToNoEscapeInst>(deallocOperand)) {
1971-
break;
1972-
} else if (auto conv = dyn_cast<ConversionInst>(deallocOperand)) {
1973-
deallocOperand = conv->getConverted();
1974-
} else {
1975-
break;
1976-
}
1977-
}
1978-
if (isa<PartialApplyInst>(deallocOperand)) {
1965+
if (auto origPA = Inst->getNonescapingClosureAllocation()) {
19791966
recordClonedInstruction(Inst,
19801967
getBuilder().createDeallocStack(getOpLocation(Inst->getLoc()),
1981-
getOpValue(deallocOperand)));
1968+
getOpValue(origPA)));
19821969
}
19831970

19841971
return;

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8193,6 +8193,10 @@ class DestroyValueInst
81938193
void setPoisonRefs(bool poisonRefs = true) {
81948194
sharedUInt8().DestroyValueInst.poisonRefs = poisonRefs;
81958195
}
8196+
8197+
/// If the value being destroyed is a stack allocation of a nonescaping
8198+
/// closure, then return the PartialApplyInst that allocated the closure.
8199+
PartialApplyInst *getNonescapingClosureAllocation() const;
81968200
};
81978201

81988202
class MoveValueInst

lib/SIL/IR/SILInstruction.cpp

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,11 +1681,11 @@ static bool visitRecursivelyLifetimeEndingUses(
16811681

16821682
// There shouldn't be any dead-end consumptions of a nonescaping
16831683
// partial_apply that don't forward it along, aside from destroy_value.
1684-
assert(use->getUser()->hasResults());
1685-
for (auto result : use->getUser()->getResults()) {
1686-
if (!visitRecursivelyLifetimeEndingUses(result, noUsers, func)) {
1687-
return false;
1688-
}
1684+
assert(use->getUser()->hasResults()
1685+
&& use->getUser()->getNumResults() == 1);
1686+
if (!visitRecursivelyLifetimeEndingUses(use->getUser()->getResult(0),
1687+
noUsers, func)) {
1688+
return false;
16891689
}
16901690
}
16911691
return true;
@@ -1705,6 +1705,53 @@ PartialApplyInst::visitOnStackLifetimeEnds(
17051705
return !noUsers;
17061706
}
17071707

1708+
PartialApplyInst *
1709+
DestroyValueInst::getNonescapingClosureAllocation() const {
1710+
SILValue operand = getOperand();
1711+
auto operandFnTy = operand->getType().getAs<SILFunctionType>();
1712+
// The query doesn't make sense if we aren't operating on a noescape closure
1713+
// to begin with.
1714+
if (!operandFnTy || !operandFnTy->isTrivialNoEscape()) {
1715+
return nullptr;
1716+
}
1717+
1718+
// Look through marker and conversion instructions that would forward
1719+
// ownership of the original partial application.
1720+
while (true) {
1721+
if (auto mdi = dyn_cast<MarkDependenceInst>(operand)) {
1722+
operand = mdi->getValue();
1723+
continue;
1724+
} else if (isa<ConvertEscapeToNoEscapeInst>(operand)
1725+
|| isa<ThinToThickFunctionInst>(operand)) {
1726+
// Stop at a conversion from escaping closure, since there's no stack
1727+
// allocation in that case.
1728+
return nullptr;
1729+
} else if (auto conv = dyn_cast<ConversionInst>(operand)) {
1730+
operand = conv->getConverted();
1731+
continue;
1732+
} else if (auto pa = dyn_cast<PartialApplyInst>(operand)) {
1733+
// If we found the `[on_stack]` partial apply, we're done.
1734+
if (pa->isOnStack()) {
1735+
return pa;
1736+
}
1737+
// Any other kind of partial apply fails to pass muster.
1738+
return nullptr;
1739+
} else {
1740+
// The original partial_apply instruction should only be forwarded
1741+
// through one of the above instructions. Anything else should lead us
1742+
// to a copy or borrow of the closure from somewhere else.
1743+
assert((isa<CopyValueInst>(operand)
1744+
|| isa<SILArgument>(operand)
1745+
|| isa<DifferentiableFunctionInst>(operand)
1746+
|| isa<DifferentiableFunctionExtractInst>(operand)
1747+
|| isa<LoadInst>(operand)
1748+
|| (operand->dump(), false))
1749+
&& "unexpected forwarding instruction for noescape closure");
1750+
return nullptr;
1751+
}
1752+
}
1753+
}
1754+
17081755
#ifndef NDEBUG
17091756

17101757
//---

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -486,30 +486,14 @@ bool OwnershipModelEliminatorVisitor::visitDestroyValueInst(
486486
// we dealloc_stack the context.
487487
auto operand = dvi->getOperand();
488488
auto operandTy = operand->getType();
489-
if (auto operandFnTy = operandTy.getAs<SILFunctionType>()) {
489+
if (auto operandFnTy = operandTy.getAs<SILFunctionType>()){
490490
if (operandFnTy->isTrivialNoEscape()) {
491-
// Look through mark_dependence and other wrapper instructions.
492-
SILValue deallocOperand = operand;
493-
while (true) {
494-
if (auto mdi = dyn_cast<MarkDependenceInst>(deallocOperand)) {
495-
deallocOperand = mdi->getValue();
496-
} else if (isa<ConvertEscapeToNoEscapeInst>(deallocOperand)) {
497-
// If there's a surviving convert_escape_to_noescape that wasn't
498-
// turned into a stack closure, then stop here and just delete the
499-
// destroy_value, since the original escaping closure's lifetime
500-
// will persist.
501-
break;
502-
} else if (auto conv = dyn_cast<ConversionInst>(deallocOperand)) {
503-
deallocOperand = conv->getConverted();
504-
} else {
505-
break;
506-
}
507-
}
508-
if (isa<PartialApplyInst>(deallocOperand)) {
491+
if (auto origPA = dvi->getNonescapingClosureAllocation()) {
509492
withBuilder<void>(dvi, [&](SILBuilder &b, SILLocation loc) {
510-
b.createDeallocStack(loc, deallocOperand);
493+
b.createDeallocStack(loc, origPA);
511494
});
512495
}
496+
513497
eraseInstruction(dvi);
514498
return true;
515499
}

0 commit comments

Comments
 (0)