Skip to content

Commit 598a541

Browse files
authored
Merge pull request swiftlang#27928 from gottesmm/pr-6389dd6f50f2b37f04a9b24d79436b133411accc
[inlining] Fix two bugs around inlining of coroutines.
2 parents 2534af7 + 4018910 commit 598a541

File tree

4 files changed

+241
-36
lines changed

4 files changed

+241
-36
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ class SILBuilder {
174174
setInsertionPoint(I);
175175
}
176176

177+
SILBuilder(SILBasicBlock *BB, const SILDebugScope *DS, SILBuilder &B)
178+
: SILBuilder(BB, DS, B.getBuilderContext()) {}
179+
177180
/// Build instructions before the given insertion point, inheriting the debug
178181
/// location.
179182
///
@@ -2264,6 +2267,11 @@ class SILBuilderWithScope : public SILBuilder {
22642267
inheritScopeFrom(InheritScopeFrom);
22652268
}
22662269

2270+
explicit SILBuilderWithScope(SILBasicBlock *BB, SILBuilder &B,
2271+
SILInstruction *InheritScopeFrom)
2272+
: SILBuilder(BB, InheritScopeFrom->getDebugScope(),
2273+
B.getBuilderContext()) {}
2274+
22672275
/// Creates a new SILBuilder with an insertion point at the
22682276
/// beginning of BB and the debug scope from the first
22692277
/// non-metainstruction in the BB.

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,10 +571,11 @@ replaceBeginApplyInst(SILBuilder &builder, SILLocation loc,
571571
SILValue token = newBAI->getTokenResult();
572572

573573
// The token will only be used by end_apply and abort_apply. Use that to
574-
// insert the end_borrows we need.
574+
// insert the end_borrows we need /after/ those uses.
575575
for (auto *use : token->getUses()) {
576-
SILBuilderWithScope borrowBuilder(use->getUser(),
577-
builder.getBuilderContext());
576+
SILBuilderWithScope borrowBuilder(
577+
&*std::next(use->getUser()->getIterator()),
578+
builder.getBuilderContext());
578579
for (SILValue borrow : newArgBorrows) {
579580
borrowBuilder.createEndBorrow(loc, borrow);
580581
}

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,20 @@ class BeginApplySite {
102102
return BeginApplySite(BeginApply, Loc, Builder);
103103
}
104104

105-
void preprocess(SILBasicBlock *returnToBB) {
105+
void preprocess(SILBasicBlock *returnToBB,
106+
SmallVectorImpl<SILInstruction *> &endBorrowInsertPts) {
106107
SmallVector<EndApplyInst *, 1> endApplyInsts;
107108
SmallVector<AbortApplyInst *, 1> abortApplyInsts;
108109
BeginApply->getCoroutineEndPoints(endApplyInsts, abortApplyInsts);
109110
while (!endApplyInsts.empty()) {
110111
auto *endApply = endApplyInsts.pop_back_val();
111112
collectEndApply(endApply);
113+
endBorrowInsertPts.push_back(&*std::next(endApply->getIterator()));
112114
}
113115
while (!abortApplyInsts.empty()) {
114-
collectAbortApply(abortApplyInsts.pop_back_val());
116+
auto *abortApply = abortApplyInsts.pop_back_val();
117+
collectAbortApply(abortApply);
118+
endBorrowInsertPts.push_back(&*std::next(abortApply->getIterator()));
115119
}
116120
}
117121

@@ -428,29 +432,38 @@ SILInlineCloner::cloneInline(ArrayRef<SILValue> AppliedArgs) {
428432

429433
SmallVector<SILValue, 4> entryArgs;
430434
entryArgs.reserve(AppliedArgs.size());
435+
SmallBitVector borrowedArgs(AppliedArgs.size());
436+
431437
auto calleeConv = getCalleeFunction()->getConventions();
432-
for (unsigned argIdx = 0, endIdx = AppliedArgs.size(); argIdx < endIdx;
433-
++argIdx) {
434-
SILValue callArg = AppliedArgs[argIdx];
438+
for (auto p : llvm::enumerate(AppliedArgs)) {
439+
SILValue callArg = p.value();
440+
unsigned idx = p.index();
435441
// Insert begin/end borrow for guaranteed arguments.
436-
if (argIdx >= calleeConv.getSILArgIndexOfFirstParam()
437-
&& calleeConv.getParamInfoForSILArg(argIdx).isGuaranteed()) {
438-
callArg = borrowFunctionArgument(callArg, Apply);
442+
if (idx >= calleeConv.getSILArgIndexOfFirstParam() &&
443+
calleeConv.getParamInfoForSILArg(idx).isGuaranteed()) {
444+
if (SILValue newValue = borrowFunctionArgument(callArg, Apply)) {
445+
callArg = newValue;
446+
borrowedArgs[idx] = true;
447+
}
439448
}
440449
entryArgs.push_back(callArg);
441450
}
442451

443452
// Create the return block and set ReturnToBB for use in visitTerminator
444453
// callbacks.
445-
SILBasicBlock *callerBB = Apply.getParent();
454+
SILBasicBlock *callerBlock = Apply.getParent();
455+
SILBasicBlock *throwBlock = nullptr;
456+
SmallVector<SILInstruction *, 1> endBorrowInsertPts;
457+
446458
switch (Apply.getKind()) {
447459
case FullApplySiteKind::ApplyInst: {
448460
auto *AI = dyn_cast<ApplyInst>(Apply);
449461

450462
// Split the BB and do NOT create a branch between the old and new
451463
// BBs; we will create the appropriate terminator manually later.
452464
ReturnToBB =
453-
callerBB->split(std::next(Apply.getInstruction()->getIterator()));
465+
callerBlock->split(std::next(Apply.getInstruction()->getIterator()));
466+
endBorrowInsertPts.push_back(&*ReturnToBB->begin());
454467

455468
// Create an argument on the return-to BB representing the returned value.
456469
auto *retArg =
@@ -459,22 +472,49 @@ SILInlineCloner::cloneInline(ArrayRef<SILValue> AppliedArgs) {
459472
AI->replaceAllUsesWith(retArg);
460473
break;
461474
}
462-
case FullApplySiteKind::BeginApplyInst:
475+
case FullApplySiteKind::BeginApplyInst: {
463476
ReturnToBB =
464-
callerBB->split(std::next(Apply.getInstruction()->getIterator()));
465-
BeginApply->preprocess(ReturnToBB);
477+
callerBlock->split(std::next(Apply.getInstruction()->getIterator()));
478+
// For begin_apply, we insert the end_borrow in the end_apply, abort_apply
479+
// blocks to ensure that our borrowed values live over both the body and
480+
// resume block of our coroutine.
481+
BeginApply->preprocess(ReturnToBB, endBorrowInsertPts);
466482
break;
467-
468-
case FullApplySiteKind::TryApplyInst:
469-
ReturnToBB = cast<TryApplyInst>(Apply)->getNormalBB();
483+
}
484+
case FullApplySiteKind::TryApplyInst: {
485+
auto *tai = cast<TryApplyInst>(Apply);
486+
ReturnToBB = tai->getNormalBB();
487+
endBorrowInsertPts.push_back(&*ReturnToBB->begin());
488+
throwBlock = tai->getErrorBB();
470489
break;
471490
}
491+
}
492+
493+
// Then insert end_borrow in our end borrow block and in the throw
494+
// block if we have one.
495+
if (borrowedArgs.any()) {
496+
for (unsigned i : indices(AppliedArgs)) {
497+
if (!borrowedArgs.test(i)) {
498+
continue;
499+
}
500+
501+
for (auto *insertPt : endBorrowInsertPts) {
502+
SILBuilderWithScope returnBuilder(insertPt, getBuilder());
503+
returnBuilder.createEndBorrow(Apply.getLoc(), entryArgs[i]);
504+
}
505+
506+
if (throwBlock) {
507+
SILBuilderWithScope throwBuilder(throwBlock->begin(), getBuilder());
508+
throwBuilder.createEndBorrow(Apply.getLoc(), entryArgs[i]);
509+
}
510+
}
511+
}
472512

473513
// Visit original BBs in depth-first preorder, starting with the
474514
// entry block, cloning all instructions and terminators.
475515
//
476516
// NextIter is initialized during `fixUp`.
477-
cloneFunctionBody(getCalleeFunction(), callerBB, entryArgs);
517+
cloneFunctionBody(getCalleeFunction(), callerBlock, entryArgs);
478518

479519
// For non-throwing applies, the inlined body now unconditionally branches to
480520
// the returned-to-code, which was previously part of the call site's basic
@@ -560,25 +600,11 @@ SILValue SILInlineCloner::borrowFunctionArgument(SILValue callArg,
560600
FullApplySite AI) {
561601
if (!AI.getFunction()->hasOwnership()
562602
|| callArg.getOwnershipKind() != ValueOwnershipKind::Owned) {
563-
return callArg;
603+
return SILValue();
564604
}
565605

566606
SILBuilderWithScope beginBuilder(AI.getInstruction(), getBuilder());
567-
auto *borrow = beginBuilder.createBeginBorrow(AI.getLoc(), callArg);
568-
if (auto *tryAI = dyn_cast<TryApplyInst>(AI)) {
569-
SILBuilderWithScope returnBuilder(tryAI->getNormalBB()->begin(),
570-
getBuilder());
571-
returnBuilder.createEndBorrow(AI.getLoc(), borrow, callArg);
572-
573-
SILBuilderWithScope throwBuilder(tryAI->getErrorBB()->begin(),
574-
getBuilder());
575-
throwBuilder.createEndBorrow(AI.getLoc(), borrow, callArg);
576-
} else {
577-
SILBuilderWithScope returnBuilder(
578-
std::next(AI.getInstruction()->getIterator()), getBuilder());
579-
returnBuilder.createEndBorrow(AI.getLoc(), borrow, callArg);
580-
}
581-
return borrow;
607+
return beginBuilder.createBeginBorrow(AI.getLoc(), callArg);
582608
}
583609

584610
void SILInlineCloner::visitDebugValueInst(DebugValueInst *Inst) {

test/SILOptimizer/mandatory_inlining_ownership.sil

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ class C {
1010
init(i: Builtin.Int64)
1111
}
1212

13+
private class C2 {
14+
var i: Builtin.Int64 { get set }
15+
init(i: Builtin.Int64)
16+
}
17+
1318
class Klass {}
1419

1520
sil [transparent] [ossa] @calleeWithGuaranteed : $@convention(thin) (@guaranteed C) -> Builtin.Int64 {
@@ -414,3 +419,168 @@ bb0(%0 : @owned $Builtin.NativeObject):
414419
%9999 = tuple()
415420
return %9999 : $()
416421
}
422+
423+
///////////////////////
424+
// Begin Apply Tests //
425+
///////////////////////
426+
427+
// Make sure that we do not violate any ownership invariants after inlining this
428+
// code.
429+
430+
sil @get_hidden_int_field_of_klass : $@convention(method) (@guaranteed Klass) -> Builtin.Int32
431+
sil @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> ()
432+
433+
sil [transparent] [ossa] @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 {
434+
bb0(%0 : @guaranteed $Klass):
435+
%2 = alloc_stack $Builtin.Int32
436+
%3 = function_ref @get_hidden_int_field_of_klass : $@convention(method) (@guaranteed Klass) -> Builtin.Int32
437+
%4 = apply %3(%0) : $@convention(method) (@guaranteed Klass) -> Builtin.Int32
438+
store %4 to [trivial] %2 : $*Builtin.Int32
439+
yield %2 : $*Builtin.Int32, resume bb1, unwind bb2
440+
441+
bb1:
442+
%7 = load [trivial] %2 : $*Builtin.Int32
443+
%8 = function_ref @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> ()
444+
%9 = apply %8(%7, %0) : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> ()
445+
dealloc_stack %2 : $*Builtin.Int32
446+
%11 = tuple ()
447+
return %11 : $()
448+
449+
bb2:
450+
%13 = load [trivial] %2 : $*Builtin.Int32
451+
%14 = function_ref @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> ()
452+
%15 = apply %14(%13, %0) : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> ()
453+
dealloc_stack %2 : $*Builtin.Int32
454+
unwind
455+
}
456+
457+
// CHECK-LABEL: sil [ossa] @begin_apply_caller : $@convention(method) (@guaranteed Klass) -> @error Error {
458+
// CHECK-NOT: begin_apply
459+
// CHECK: } // end sil function 'begin_apply_caller'
460+
sil [ossa] @begin_apply_caller : $@convention(method) (@guaranteed Klass) -> @error Error {
461+
bb0(%0 : @guaranteed $Klass):
462+
%6 = copy_value %0 : $Klass
463+
%12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
464+
(%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
465+
end_apply %14
466+
destroy_value %6 : $Klass
467+
%19 = tuple ()
468+
return %19 : $()
469+
}
470+
471+
// CHECK-LABEL: sil [ossa] @begin_apply_caller_2 : $@convention(method) (@guaranteed Klass) -> @error Error {
472+
// CHECK-NOT: begin_apply
473+
// CHECK: } // end sil function 'begin_apply_caller_2'
474+
sil [ossa] @begin_apply_caller_2 : $@convention(method) (@guaranteed Klass) -> @error Error {
475+
bb0(%0 : @guaranteed $Klass):
476+
%6 = copy_value %0 : $Klass
477+
%12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
478+
(%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
479+
abort_apply %14
480+
destroy_value %6 : $Klass
481+
%19 = tuple ()
482+
return %19 : $()
483+
}
484+
485+
// CHECK-LABEL: sil [ossa] @begin_apply_caller_3 : $@convention(method) (@guaranteed Klass) -> @error Error {
486+
// CHECK-NOT: begin_apply
487+
// CHECK: } // end sil function 'begin_apply_caller_3'
488+
sil [ossa] @begin_apply_caller_3 : $@convention(method) (@guaranteed Klass) -> @error Error {
489+
bb0(%0 : @guaranteed $Klass):
490+
%6 = copy_value %0 : $Klass
491+
%12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
492+
(%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32
493+
cond_br undef, bb1, bb2
494+
495+
bb1:
496+
end_apply %14
497+
br bb3
498+
499+
bb2:
500+
abort_apply %14
501+
br bb3
502+
503+
bb3:
504+
destroy_value %6 : $Klass
505+
%19 = tuple ()
506+
return %19 : $()
507+
}
508+
509+
sil [ossa] [transparent] @devirt_callee : $@yield_once @convention(method) (@guaranteed C) -> @yields @inout Builtin.Int64 {
510+
bb0(%0 : @guaranteed $C):
511+
%1 = alloc_stack $Builtin.Int64
512+
%1a = integer_literal $Builtin.Int64, 0
513+
store %1a to [trivial] %1 : $*Builtin.Int64
514+
yield %1 : $*Builtin.Int64, resume bb1, unwind bb2
515+
516+
bb1:
517+
dealloc_stack %1 : $*Builtin.Int64
518+
%6 = tuple ()
519+
return %6 : $()
520+
521+
bb2:
522+
dealloc_stack %1 : $*Builtin.Int64
523+
unwind
524+
}
525+
526+
// Just make sure we actually inlined the begin_apply. We just want to make sure
527+
// we are not breaking ownership invariants by not properly borrowing %0.
528+
// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller : $@convention(method) (@owned C2) -> @error Error {
529+
// CHECK-NOT: begin_apply
530+
// CHECK: } // end sil function 'begin_apply_devirt_caller'
531+
sil [ossa] @begin_apply_devirt_caller : $@convention(method) (@owned C2) -> @error Error {
532+
bb0(%0 : @owned $C2):
533+
%1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
534+
(%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
535+
br bb1
536+
537+
bb1:
538+
end_apply %tok
539+
destroy_value %0 : $C2
540+
%9999 = tuple()
541+
return %9999 : $()
542+
}
543+
544+
// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller_2 : $@convention(method) (@owned C2) -> @error Error {
545+
// CHECK-NOT: begin_apply
546+
// CHECK: } // end sil function 'begin_apply_devirt_caller_2'
547+
sil [ossa] @begin_apply_devirt_caller_2 : $@convention(method) (@owned C2) -> @error Error {
548+
bb0(%0 : @owned $C2):
549+
%1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
550+
(%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
551+
br bb1
552+
553+
bb1:
554+
abort_apply %tok
555+
destroy_value %0 : $C2
556+
%9999 = tuple()
557+
return %9999 : $()
558+
}
559+
560+
// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller_3 : $@convention(method) (@owned C2) -> @error Error {
561+
// CHECK-NOT: begin_apply
562+
// CHECK: } // end sil function 'begin_apply_devirt_caller_3'
563+
sil [ossa] @begin_apply_devirt_caller_3 : $@convention(method) (@owned C2) -> @error Error {
564+
bb0(%0 : @owned $C2):
565+
%1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
566+
(%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64
567+
cond_br undef, bb1, bb2
568+
569+
bb1:
570+
end_apply %tok
571+
br bb3
572+
573+
bb2:
574+
abort_apply %tok
575+
br bb3
576+
577+
bb3:
578+
destroy_value %0 : $C2
579+
%9999 = tuple()
580+
return %9999 : $()
581+
}
582+
583+
584+
sil_vtable C2 {
585+
#C2.i!modify.1: (C2) -> () -> () : @devirt_callee
586+
}

0 commit comments

Comments
 (0)