Skip to content

Commit a0b1330

Browse files
authored
Merge pull request #70333 from jckarter/read-coroutines-yielding-noncopyable-values
Move-only-check the yielded result from read coroutines when they're noncopyable.
2 parents b709fa2 + 96c87db commit a0b1330

File tree

6 files changed

+274
-33
lines changed

6 files changed

+274
-33
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4822,17 +4822,28 @@ namespace {
48224822
/// Cleanup to end a coroutine application.
48234823
class EndCoroutineApply : public Cleanup {
48244824
SILValue ApplyToken;
4825+
std::vector<BeginBorrowInst *> BorrowedMoveOnlyValues;
48254826
public:
48264827
EndCoroutineApply(SILValue applyToken) : ApplyToken(applyToken) {}
48274828

4829+
void setBorrowedMoveOnlyValues(ArrayRef<BeginBorrowInst *> values) {
4830+
BorrowedMoveOnlyValues.insert(BorrowedMoveOnlyValues.end(),
4831+
values.begin(), values.end());
4832+
}
4833+
48284834
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override {
4835+
for (auto i = BorrowedMoveOnlyValues.rbegin(), e = BorrowedMoveOnlyValues.rend();
4836+
i != e; ++i) {
4837+
SGF.B.createEndBorrow(l, *i);
4838+
SGF.B.createDestroyValue(l, (*i)->getOperand());
4839+
}
48294840
if (forUnwind) {
48304841
SGF.B.createAbortApply(l, ApplyToken);
48314842
} else {
48324843
SGF.B.createEndApply(l, ApplyToken);
48334844
}
48344845
}
4835-
4846+
48364847
void dump(SILGenFunction &SGF) const override {
48374848
#ifndef NDEBUG
48384849
llvm::errs() << "EndCoroutineApply "
@@ -4897,23 +4908,51 @@ SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn,
48974908
auto yieldInfos = substFnType->getYields();
48984909
assert(yieldValues.size() == yieldInfos.size());
48994910
bool useLoweredAddresses = silConv.useLoweredAddresses();
4911+
SmallVector<BeginBorrowInst *, 2> borrowedMoveOnlyValues;
49004912
for (auto i : indices(yieldValues)) {
49014913
auto value = yieldValues[i];
49024914
auto info = yieldInfos[i];
49034915
if (info.isIndirectInOut()) {
49044916
yields.push_back(ManagedValue::forLValue(value));
49054917
} else if (info.isConsumed()) {
4918+
if (value->getType().isMoveOnly()) {
4919+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4920+
MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable);
4921+
}
49064922
!useLoweredAddresses && value->getType().isTrivial(getFunction())
49074923
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
49084924
: yields.push_back(emitManagedRValueWithCleanup(value));
49094925
} else if (info.isGuaranteed()) {
4910-
!useLoweredAddresses && value->getType().isTrivial(getFunction())
4911-
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
4912-
: yields.push_back(ManagedValue::forBorrowedRValue(value));
4926+
if (value->getType().isMoveOnly()) {
4927+
if (!value->getType().isAddress()) {
4928+
// The move checker uses the lifetime of the "copy" for borrow checking.
4929+
value = B.createCopyValue(loc, value);
4930+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4931+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
4932+
auto borrow = B.createBeginBorrow(loc, value);
4933+
yields.push_back(ManagedValue::forBorrowedRValue(borrow));
4934+
borrowedMoveOnlyValues.push_back(borrow);
4935+
} else {
4936+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4937+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
4938+
yields.push_back(ManagedValue::forRValueWithoutOwnership(value));
4939+
}
4940+
} else {
4941+
!useLoweredAddresses && value->getType().isTrivial(getFunction())
4942+
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
4943+
: yields.push_back(ManagedValue::forBorrowedRValue(value));
4944+
}
49134945
} else {
4946+
assert(!value->getType().isMoveOnly()
4947+
&& "move-only types shouldn't be trivial");
49144948
yields.push_back(ManagedValue::forRValueWithoutOwnership(value));
49154949
}
49164950
}
4951+
4952+
if (!borrowedMoveOnlyValues.empty()) {
4953+
auto &endApply = static_cast<EndCoroutineApply &>(Cleanups.getCleanup(endApplyHandle));
4954+
endApply.setBorrowedMoveOnlyValues(borrowedMoveOnlyValues);
4955+
}
49174956

49184957
return endApplyHandle;
49194958
}

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,9 @@ void UseState::initializeLiveness(
950950
reinitInstAndValue.second);
951951
}
952952
}
953+
954+
// FIXME: Whether the initial use is an initialization ought to be entirely
955+
// derivable from the CheckKind of the mark instruction.
953956

954957
// Then check if our markedValue is from an argument that is in,
955958
// in_guaranteed, inout, or inout_aliasable, consider the marked address to be
@@ -1048,6 +1051,12 @@ void UseState::initializeLiveness(
10481051
recordInitUse(address, address, liveness.getTopLevelSpan());
10491052
liveness.initializeDef(address, liveness.getTopLevelSpan());
10501053
}
1054+
1055+
if (auto *bai = dyn_cast_or_null<BeginApplyInst>(
1056+
stripAccessMarkers(address->getOperand())->getDefiningInstruction())) {
1057+
recordInitUse(address, address, liveness.getTopLevelSpan());
1058+
liveness.initializeDef(address, liveness.getTopLevelSpan());
1059+
}
10511060

10521061
// Now that we have finished initialization of defs, change our multi-maps
10531062
// from their array form to their map form.

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,21 @@ static void getVariableNameForValue(SILValue value2,
113113
break;
114114
}
115115

116+
if (auto bai = dyn_cast_or_null<BeginApplyInst>(searchValue->getDefiningInstruction())) {
117+
// Use the name of the property being accessed if we can get to it.
118+
if (isa<FunctionRefBaseInst>(bai->getCallee())
119+
|| isa<MethodInst>(bai->getCallee())) {
120+
variableNamePath.push_back(bai->getCallee()->getDefiningInstruction());
121+
// Try to name the base of the property if this is a method.
122+
if (bai->getSubstCalleeType()->hasSelfParam()) {
123+
searchValue = bai->getSelfArgument();
124+
continue;
125+
} else {
126+
break;
127+
}
128+
}
129+
}
130+
116131
// If we do not do an exact match, see if we can find a debug_var inst. If
117132
// we do, we always break since we have a root value.
118133
if (auto *use = getAnyDebugUse(searchValue)) {
@@ -132,12 +147,25 @@ static void getVariableNameForValue(SILValue value2,
132147
searchValue = cast<SingleValueInstruction>(searchValue)->getOperand(0);
133148
continue;
134149
}
135-
150+
136151
// If we do not pattern match successfully, just set resulting string to
137152
// unknown and return early.
138153
resultingString += "unknown";
139154
return;
140155
}
156+
157+
auto nameFromDecl = [&](Decl *d) -> StringRef {
158+
if (d) {
159+
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
160+
return accessor->getStorage()->getBaseName().userFacingName();
161+
}
162+
if (auto vd = dyn_cast<ValueDecl>(d)) {
163+
return vd->getBaseName().userFacingName();
164+
}
165+
}
166+
167+
return "<unknown decl>";
168+
};
141169

142170
// Walk backwards, constructing our string.
143171
while (true) {
@@ -148,6 +176,16 @@ static void getVariableNameForValue(SILValue value2,
148176
resultingString += i.getName();
149177
} else if (auto i = VarDeclCarryingInst(inst)) {
150178
resultingString += i.getName();
179+
} else if (auto f = dyn_cast<FunctionRefBaseInst>(inst)) {
180+
if (auto dc = f->getInitiallyReferencedFunction()->getDeclContext()) {
181+
resultingString += nameFromDecl(dc->getAsDecl());
182+
} else {
183+
resultingString += "<unknown decl>";
184+
}
185+
} else if (auto m = dyn_cast<MethodInst>(inst)) {
186+
resultingString += nameFromDecl(m->getMember().getDecl());
187+
} else {
188+
resultingString += "<unknown decl>";
151189
}
152190
} else {
153191
auto value = next.get<SILValue>();

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,11 @@ void MoveOnlyObjectCheckerPImpl::check(DominanceInfo *domTree,
481481
i = copyToMoveOnly;
482482
}
483483

484+
// TODO: Instead of pattern matching specific code generation patterns,
485+
// we should be able to eliminate any `copy_value` whose canonical
486+
// lifetime fits within the borrow scope of the borrowed value being
487+
// copied from.
488+
484489
// Handle:
485490
//
486491
// bb0(%0 : @guaranteed $Type):
@@ -521,6 +526,24 @@ void MoveOnlyObjectCheckerPImpl::check(DominanceInfo *domTree,
521526
}
522527
}
523528
}
529+
530+
// Handle:
531+
// (%yield, ..., %handle) = begin_apply
532+
// %copy = copy_value %yield
533+
// %mark = mark_unresolved_noncopyable_value [no_consume_or_assign] %copy
534+
if (auto bai = dyn_cast_or_null<BeginApplyInst>(i->getOperand(0)->getDefiningInstruction())) {
535+
if (i->getOperand(0)->getOwnershipKind() == OwnershipKind::Guaranteed) {
536+
for (auto *use : markedInst->getConsumingUses()) {
537+
destroys.push_back(cast<DestroyValueInst>(use->getUser()));
538+
}
539+
while (!destroys.empty())
540+
destroys.pop_back_val()->eraseFromParent();
541+
markedInst->replaceAllUsesWith(i->getOperand(0));
542+
markedInst->eraseFromParent();
543+
cvi->eraseFromParent();
544+
continue;
545+
}
546+
}
524547
}
525548
}
526549
}

0 commit comments

Comments
 (0)