Skip to content

Commit 05fe36c

Browse files
authored
Merge pull request #68056 from atrick/5.9-fix-closure-moveonly-arg
[5.9] Fix compiler crashes with consuming and borrowing keywords.
2 parents aba4e70 + 40cfbee commit 05fe36c

File tree

6 files changed

+126
-43
lines changed

6 files changed

+126
-43
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 12 additions & 5 deletions
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/SILOptimizer/Mandatory/CapturePromotion.cpp

Lines changed: 3 additions & 1 deletion
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

Lines changed: 31 additions & 11 deletions
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/Utils/InstOptUtils.cpp

Lines changed: 22 additions & 26 deletions
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,
@@ -1468,7 +1469,7 @@ swift::findLocalApplySites(FunctionRefBaseInst *fri) {
14681469
/// Insert destroys of captured arguments of partial_apply [stack].
14691470
void swift::insertDestroyOfCapturedArguments(
14701471
PartialApplyInst *pai, SILBuilder &builder,
1471-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy,
1472+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy,
14721473
SILLocation origLoc) {
14731474
assert(pai->isOnStack());
14741475

@@ -1477,27 +1478,25 @@ void swift::insertDestroyOfCapturedArguments(
14771478
pai->getModule());
14781479
auto loc = CleanupLocation(origLoc);
14791480
for (auto &arg : pai->getArgumentOperands()) {
1480-
SILValue argValue = arg.get();
1481-
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
1482-
if (m->hasGuaranteedInitialKind())
1483-
argValue = m->getOperand();
1484-
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
1485-
if (argBorrow) {
1486-
argValue = argBorrow->getOperand();
1487-
builder.createEndBorrow(loc, argBorrow);
1488-
}
1489-
if (!shouldInsertDestroy(argValue))
1481+
SILValue argValue = getValueToDestroy(arg.get());
1482+
if (!argValue)
14901483
continue;
1484+
1485+
assert(!pai->getFunction()->hasOwnership()
1486+
|| (argValue->getOwnershipKind().isCompatibleWith(
1487+
OwnershipKind::Owned)));
1488+
14911489
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
14921490
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
14931491
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
14941492
releasePartialApplyCapturedArg(builder, loc, argValue, paramInfo);
14951493
}
14961494
}
14971495

1498-
void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
1499-
DominanceInfo *domInfo,
1500-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy)
1496+
void swift::insertDeallocOfCapturedArguments(
1497+
PartialApplyInst *pai,
1498+
DominanceInfo *domInfo,
1499+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc)
15011500
{
15021501
assert(pai->isOnStack());
15031502

@@ -1511,13 +1510,10 @@ void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
15111510
if (!paramInfo.isIndirectInGuaranteed())
15121511
continue;
15131512

1514-
SILValue argValue = arg.get();
1515-
if (!shouldInsertDestroy(argValue)) {
1513+
SILValue argValue = getAddressToDealloc(arg.get());
1514+
if (!argValue) {
15161515
continue;
15171516
}
1518-
if (auto moveWrapper =
1519-
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argValue))
1520-
argValue = moveWrapper->getOperand();
15211517

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

test/SILOptimizer/closure-lifetime-fixup.sil

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,41 @@ bb3(%25 : @owned $any Error):
310310
bb4(%29 : @owned $any Error):
311311
unreachable
312312
}
313+
314+
sil @testTrivialBorrowClosure : $@convention(thin) (Int) -> Int
315+
sil @testTrivialBorrowTakeClosure : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
316+
317+
// rdar://112795074 (Crash compiling function that has a macro annotation and uses `consuming`)
318+
//
319+
// CHECK-LABEL: sil [ossa] @testTrivialBorrow : $@convention(thin) (Int) -> Int {
320+
// CHECK: [[ARG:%.*]] = moveonlywrapper_to_copyable [guaranteed]
321+
// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] %{{.*}}([[ARG]]) : $@convention(thin) (Int) -> Int
322+
// CHECK-NOT: destroy
323+
// CHECK: destroy_value [[PA]] : $@noescape @callee_guaranteed () -> Int
324+
// CHECK-NOT: destroy
325+
// CHECK: destroy_addr
326+
// CHECK-NEXT: dealloc_stack
327+
// CHECK-NEXT: return
328+
// CHECK-LABEL: } // end sil function 'testTrivialBorrow'
329+
sil [ossa] @testTrivialBorrow : $@convention(thin) (Int) -> Int {
330+
bb0(%0 : @noImplicitCopy @_eagerMove $Int):
331+
%1 = alloc_stack $@moveOnly Int, var, name "i"
332+
%2 = mark_must_check [consumable_and_assignable] %1 : $*@moveOnly Int
333+
%3 = moveonlywrapper_to_copyable_addr %2 : $*@moveOnly Int
334+
store %0 to [trivial] %3 : $*Int
335+
%5 = begin_access [read] [static] %2 : $*@moveOnly Int
336+
%6 = load_borrow %5 : $*@moveOnly Int
337+
%7 = moveonlywrapper_to_copyable [guaranteed] %6 : $@moveOnly Int
338+
end_borrow %6 : $@moveOnly Int
339+
end_access %5 : $*@moveOnly Int
340+
%10 = function_ref @testTrivialBorrowClosure : $@convention(thin) (Int) -> Int
341+
%11 = partial_apply [callee_guaranteed] %10(%7) : $@convention(thin) (Int) -> Int
342+
%12 = convert_escape_to_noescape [not_guaranteed] %11 : $@callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> Int
343+
%13 = function_ref @testTrivialBorrowTakeClosure : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
344+
%14 = apply %13(%12) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
345+
destroy_value %12 : $@noescape @callee_guaranteed () -> Int
346+
destroy_value %11 : $@callee_guaranteed () -> Int
347+
destroy_addr %2 : $*@moveOnly Int
348+
dealloc_stack %1 : $*@moveOnly Int
349+
return %14 : $Int
350+
}

test/SILOptimizer/noimplicitcopy_trivial.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,3 +1078,23 @@ public func noImplicitCopyReturnUse(_ x: Int) -> Int {
10781078
let _ = z
10791079
return y // expected-note {{consumed again here}}
10801080
}
1081+
1082+
func takeClosure(_ f: ()->Int) -> Int { f() }
1083+
1084+
public func test1(i: consuming Int) -> Int {
1085+
takeClosure { [i = copy i] in i }
1086+
}
1087+
1088+
public func test2(i: borrowing Int) -> Int {
1089+
takeClosure { [i = copy i] in i }
1090+
}
1091+
1092+
public func test3(i: consuming Int) -> Int {
1093+
takeClosure { i }
1094+
}
1095+
1096+
// TODO: incorrect diagnostic:
1097+
// error: 'i' cannot be captured by an escaping closure since it is a borrowed parameter
1098+
// public func test4(i: borrowing Int) -> Int {
1099+
// takeClosure { i }
1100+
// }

0 commit comments

Comments
 (0)