Merge pull request #17026 from aschwaighofer/wip_begin_apply_inlining
SILInliner: Initial support for begin_apply
diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
index 9a714ac..37ec91c 100644
--- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
+++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
@@ -656,12 +656,7 @@
// Returns the callee of an apply_inst if it is basically inlinable.
SILFunction *swift::getEligibleFunction(FullApplySite AI,
InlineSelection WhatToInline) {
- // For now, we cannot inline begin_apply at all.
- if (isa<BeginApplyInst>(AI))
- return nullptr;
-
SILFunction *Callee = AI.getReferencedFunction();
- SILFunction *EligibleCallee = nullptr;
if (!Callee) {
return nullptr;
@@ -776,8 +771,7 @@
return nullptr;
}
- EligibleCallee = Callee;
- return EligibleCallee;
+ return Callee;
}
/// Returns true if the instruction \I has any interesting side effects which
diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp
index dbd20ce..b289663 100644
--- a/lib/SILOptimizer/Utils/SILInliner.cpp
+++ b/lib/SILOptimizer/Utils/SILInliner.cpp
@@ -18,13 +18,181 @@
using namespace swift;
bool SILInliner::canInlineFunction(FullApplySite AI) {
- // For now, we cannot inline begin_apply at all.
- if (isa<BeginApplyInst>(AI))
- return false;
-
return AI.getFunction() != &Original;
}
+/// Utility class for rewiring control-flow of inlined begin_apply functions.
+class BeginApplySite {
+ SmallVector<SILBasicBlock *, 4> ExitingBlocks;
+ SmallVector<AllocStackInst*, 8> YieldedIndirectValues;
+ SILLocation Loc;
+ SILBuilder &Builder;
+ BeginApplyInst *BeginApply;
+ SILFunction *F;
+ EndApplyInst *EndApply = nullptr;
+ SILBasicBlock *EndApplyBB = nullptr;
+ SILBasicBlock *EndApplyBBMerge = nullptr;
+ AbortApplyInst *AbortApply = nullptr;
+ SILBasicBlock *AbortApplyBB = nullptr;
+ SILBasicBlock *AbortApplyBBMerge = nullptr;
+ SILArgument *IntToken = nullptr;
+
+ unsigned YieldNum = 0;
+ SmallVector<SILBasicBlock*, 8> YieldResumes;
+ SmallVector<SILBasicBlock*, 8> YieldUnwinds;
+
+ void
+ getYieldCaseBBs(SmallVectorImpl<std::pair<SILValue, SILBasicBlock *>> &Result,
+ SmallVectorImpl<SILBasicBlock *> &Dests) {
+ unsigned Token = 0;
+ for (auto *Blk : Dests) {
+ Result.push_back(std::make_pair(
+ SILValue(Builder.createIntegerLiteral(
+ Loc,
+ SILType::getBuiltinIntegerType(
+ 32, Builder.getFunction().getModule().getASTContext()),
+ Token++)),
+ Blk));
+ }
+ }
+
+public:
+ BeginApplySite(BeginApplyInst *BeginApply, SILLocation Loc,
+ SILBuilder &Builder)
+ : Loc(Loc), Builder(Builder), BeginApply(BeginApply),
+ F(BeginApply->getFunction()) {}
+
+ static Optional<BeginApplySite> isa(FullApplySite AI, SILLocation Loc,
+ SILBuilder &Builder) {
+ auto *BeginApply = dyn_cast<BeginApplyInst>(AI);
+ if (!BeginApply)
+ return None;
+ return BeginApplySite(BeginApply, Loc, Builder);
+ }
+
+ void collectCallerExitingBlocks() {
+ F->findExitingBlocks(ExitingBlocks);
+ }
+
+ void processApply(SILBasicBlock *ReturnToBB) {
+ // Handle direct and indirect results.
+ for (auto YieldedValue : BeginApply->getYieldedValues()) {
+ // Insert an alloc_stack for indirect results.
+ if (YieldedValue->getType().isAddress()) {
+ Builder.setInsertionPoint(F->getEntryBlock()->begin());
+ auto Addr = Builder.createAllocStack(
+ Loc, YieldedValue->getType().getObjectType());
+ YieldedValue->replaceAllUsesWith(Addr);
+ YieldedIndirectValues.push_back(Addr);
+ for (auto *Exit : ExitingBlocks) {
+ Builder.setInsertionPoint(Exit->getTerminator());
+ Builder.createDeallocStack(Loc, Addr);
+ }
+ continue;
+ }
+ // Insert a phi for direct results.
+ auto *RetArg = ReturnToBB->createPHIArgument(YieldedValue->getType(),
+ ValueOwnershipKind::Owned);
+ // Replace all uses of the ApplyInst with the new argument.
+ YieldedValue->replaceAllUsesWith(RetArg);
+ }
+
+ // Add a trailing phi argument for the token integer (tells us which yield
+ // we came from).
+ IntToken = ReturnToBB->createPHIArgument(
+ SILType::getBuiltinIntegerType(32, F->getModule().getASTContext()),
+ ValueOwnershipKind::Owned);
+
+ // Get the end_apply, abort_apply instructions.
+ auto Token = BeginApply->getTokenResult();
+ for (auto *TokenUse : Token->getUses()) {
+ EndApply = dyn_cast<EndApplyInst>(TokenUse->getUser());
+ if (EndApply)
+ continue;
+ AbortApply = cast<AbortApplyInst>(TokenUse->getUser());
+ }
+
+ // Split the basic block before the end/abort_apply. We will insert code
+ // to jump to the resume/unwind blocks depending on the integer token
+ // later. And the inlined resume/unwind return blocks will jump back to
+ // the merge blocks.
+ EndApplyBB = EndApply->getParent();
+ EndApplyBBMerge = EndApplyBB->split(SILBasicBlock::iterator(EndApply));
+ if (AbortApply) {
+ AbortApplyBB = AbortApply->getParent();
+ AbortApplyBBMerge =
+ AbortApplyBB->split(SILBasicBlock::iterator(AbortApply));
+ }
+ }
+
+ void processTerminator(
+ TermInst *Terminator, SILBasicBlock *ReturnToBB,
+ llvm::function_ref<SILBasicBlock *(SILBasicBlock *)> remapBlock,
+ llvm::function_ref<SILValue(SILValue)> remapValue,
+ llvm::function_ref<void(TermInst *)> mapTerminator) {
+ // A yield branches to the begin_apply return block passing the yielded
+ // results as branch arguments. Collect the yields target block for
+ // resuming later. Pass an integer token to the begin_apply return block
+ // to mark the yield we came from.
+ if (auto *Yield = dyn_cast<YieldInst>(Terminator)) {
+ YieldResumes.push_back(remapBlock(Yield->getResumeBB()));
+ YieldUnwinds.push_back(remapBlock(Yield->getUnwindBB()));
+ auto ContextToken = Builder.createIntegerLiteral(
+ Loc,
+ SILType::getBuiltinIntegerType(32, F->getModule().getASTContext()),
+ YieldNum++);
+
+ SmallVector<SILValue, 8> BrResults;
+ unsigned IndirectIdx = 0;
+ for (auto CalleeYieldedVal : Yield->getYieldedValues()) {
+ auto YieldedVal = remapValue(CalleeYieldedVal);
+ if (YieldedVal->getType().isAddress()) {
+ auto YieldedDestAddr = YieldedIndirectValues[IndirectIdx++];
+ Builder.createCopyAddr(Loc, YieldedVal, YieldedDestAddr, IsTake,
+ IsInitialization);
+ } else
+ BrResults.push_back(YieldedVal);
+ }
+ BrResults.push_back(SILValue(ContextToken));
+ Builder.createBranch(Loc, ReturnToBB, BrResults);
+ return;
+ }
+
+ // Return and unwind terminators branch to the end_apply/abort_apply merge
+ // block respectively.
+ if (auto *RI = dyn_cast<ReturnInst>(Terminator)) {
+ Builder.createBranch(Loc, EndApplyBBMerge);
+ return;
+ }
+ if (auto *Unwind = dyn_cast<UnwindInst>(Terminator)) {
+ Builder.createBranch(Loc, AbortApplyBBMerge);
+ return;
+ }
+
+ // Otherwise, we just map the branch instruction.
+ assert(!::isa<ThrowInst>(Terminator) &&
+ "Unexpected throw instruction in yield_once function");
+ mapTerminator(Terminator);
+ }
+
+ void dispatchToResumeUnwindBlocks() {
+ // Resume edge.
+ Builder.setInsertionPoint(EndApplyBB);
+ SmallVector<std::pair<SILValue, SILBasicBlock *>, 8> CaseBBs;
+ getYieldCaseBBs(CaseBBs, YieldResumes);
+ Builder.createSwitchValue(Loc, IntToken, nullptr, CaseBBs);
+ EndApply->eraseFromParent();
+ // Unwind edge.
+ if (AbortApplyBB) {
+ Builder.setInsertionPoint(AbortApplyBB);
+ SmallVector<std::pair<SILValue, SILBasicBlock *>, 8> CaseBBs;
+ getYieldCaseBBs(CaseBBs, YieldUnwinds);
+ Builder.createSwitchValue(Loc, IntToken, nullptr, CaseBBs);
+ AbortApply->eraseFromParent();
+ }
+ }
+};
+
/// \brief Inlines the callee of a given ApplyInst (which must be the value of a
/// FunctionRefInst referencing a function with a known body), into the caller
/// containing the ApplyInst, which must be the same function as provided to the
@@ -114,6 +282,12 @@
ValueMap.insert(std::make_pair(calleeArg, callArg));
}
+ // Find the existing blocks. We will need them for inlining the co-routine
+ // call.
+ auto BeginApply = BeginApplySite::isa(AI, Loc.getValue(), getBuilder());
+ if (BeginApply)
+ BeginApply->collectCallerExitingBlocks();
+
// Recursively visit callee's BB in depth-first preorder, starting with the
// entry block, cloning all instructions other than terminators.
visitSILBasicBlock(CalleeEntryBB);
@@ -131,6 +305,7 @@
// If we're inlining into a try_apply, we already have a return-to BB.
SILBasicBlock *ReturnToBB;
+
if (auto tryAI = dyn_cast<TryApplyInst>(AI)) {
ReturnToBB = tryAI->getNormalBB();
@@ -149,17 +324,35 @@
SILFunction::iterator(ReturnToBB));
// Create an argument on the return-to BB representing the returned value.
- auto apply = cast<ApplyInst>(AI.getInstruction());
- auto *RetArg = ReturnToBB->createPHIArgument(apply->getType(),
- ValueOwnershipKind::Owned);
- // Replace all uses of the ApplyInst with the new argument.
- apply->replaceAllUsesWith(RetArg);
+ if (auto apply = dyn_cast<ApplyInst>(AI.getInstruction())) {
+ auto *RetArg = ReturnToBB->createPHIArgument(apply->getType(),
+ ValueOwnershipKind::Owned);
+ // Replace all uses of the ApplyInst with the new argument.
+ apply->replaceAllUsesWith(RetArg);
+ } else {
+ // Handle begin_apply.
+ BeginApply->processApply(ReturnToBB);
+ }
}
// Now iterate over the callee BBs and fix up the terminators.
for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) {
getBuilder().setInsertionPoint(BI->second);
+ // Coroutine terminators need special handling.
+ if (BeginApply) {
+ BeginApply->processTerminator(
+ BI->first->getTerminator(), ReturnToBB,
+ [=](SILBasicBlock *Block) -> SILBasicBlock * {
+ return this->remapBasicBlock(Block);
+ },
+ [=](SILValue Val) -> SILValue {
+ return this->remapValue(Val);
+ },
+ [=](TermInst *Term) { this->visit(Term); });
+ continue;
+ }
+
// Modify return terminators to branch to the return-to BB, rather than
// trying to clone the ReturnInst.
if (auto *RI = dyn_cast<ReturnInst>(BI->first->getTerminator())) {
@@ -190,6 +383,10 @@
// but remaps basic blocks and values.
visit(BI->first->getTerminator());
}
+
+ // Insert dispatch code at end/abort_apply to the resume/unwind target blocks.
+ if (BeginApply)
+ BeginApply->dispatchToResumeUnwindBlocks();
}
SILValue SILInliner::borrowFunctionArgument(SILValue callArg,
diff --git a/test/SILOptimizer/inline_begin_apply.sil b/test/SILOptimizer/inline_begin_apply.sil
new file mode 100644
index 0000000..5aec6e5
--- /dev/null
+++ b/test/SILOptimizer/inline_begin_apply.sil
@@ -0,0 +1,350 @@
+// RUN: %target-sil-opt -inline -verify %s | %FileCheck %s
+// RUN: %target-sil-opt -mandatory-inlining -verify %s | %FileCheck %s
+
+import Builtin
+import Swift
+
+sil @marker : $(Builtin.Int32) -> ()
+
+class SomeClass {}
+sil_vtable SomeClass {}
+
+class SomeSubclass : SomeClass {}
+sil_vtable SomeSubclass {}
+
+// This is designed to be formally indirect.
+struct Indirect<T: AnyObject> {
+ var x: Any
+ var y: T
+}
+
+sil @make_indirect : $<T: SomeClass> () -> (@out Indirect<T>)
+
+sil [transparent] @test_one_yield : $@yield_once <C: SomeClass> () -> (@yields @in Indirect<C>) {
+entry:
+ %marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+ %1000 = integer_literal $Builtin.Int32, 1000
+ apply %marker(%1000) : $@convention(thin) (Builtin.Int32) -> ()
+ %temp = alloc_stack $Indirect<C>
+ %make = function_ref @make_indirect : $@convention(thin) <T: SomeClass> () -> (@out Indirect<T>)
+ apply %make<C>(%temp) : $@convention(thin) <T: SomeClass> () -> (@out Indirect<T>)
+ yield %temp : $*Indirect<C>, resume resume, unwind unwind
+
+resume:
+ %2000 = integer_literal $Builtin.Int32, 2000
+ apply %marker(%2000) : $@convention(thin) (Builtin.Int32) -> ()
+ dealloc_stack %temp : $*Indirect<C>
+ %ret = tuple ()
+ return %ret : $()
+
+unwind:
+ %3000 = integer_literal $Builtin.Int32, 3000
+ apply %marker(%3000) : $@convention(thin) (Builtin.Int32) -> ()
+ dealloc_stack %temp : $*Indirect<C>
+ unwind
+}
+
+// CHECK-LABEL: sil @test_simple_call
+// CHECK: bb0(%0 : $Builtin.Int1):
+// CHECK: [[IND_RES:%.*]] = alloc_stack $Indirect<SomeSubclass>
+// CHECK: [[MARKER:%.*]] = function_ref @marker
+// CHECK: [[MARKER2:%.*]] = function_ref @marker
+// CHECK: [[I:%.*]] = integer_literal $Builtin.Int32, 1000
+// CHECK: apply [[MARKER2]]([[I]]) : $@convention(thin) (Builtin.Int32) -> ()
+// CHECK: [[TEMP:%.*]] = alloc_stack $Indirect<SomeSubclass>
+// CHECK: [[MK_IND:%.*]] = function_ref @make_indirect
+// CHECK: apply [[MK_IND]]<SomeSubclass>([[TEMP]])
+// CHECK: [[INTTOKEN:%.*]] = integer_literal $Builtin.Int32, 0
+// CHECK: copy_addr [take] [[TEMP]] to [initialization] [[IND_RES]] : $*Indirect<SomeSubclass>
+// CHECK: br bb3([[INTTOKEN]] : $Builtin.Int32)
+
+// CHECK:bb1:
+// CHECK: [[I2:%.*]] = integer_literal $Builtin.Int32, 2000
+// CHECK: apply [[MARKER2]]([[I2]])
+// CHECK: dealloc_stack [[TEMP]] : $*Indirect<SomeSubclass>
+// CHECK: br bb5
+
+// CHECK: bb2:
+// CHECK: [[I3:%.*]] = integer_literal $Builtin.Int32, 3000
+// CHECK: apply [[MARKER2]]([[I3]])
+// CHECK: dealloc_stack [[TEMP]] : $*Indirect<SomeSubclass>
+// CHECK: br bb7
+
+// CHECK: bb3([[WHICH_YIELD:%.*]] : $Builtin.Int32):
+// CHECK: destroy_addr [[IND_RES]] : $*Indirect<SomeSubclass>
+// CHECK: cond_br %0, bb4, bb6
+
+// CHECK: bb4:
+// CHECK: [[I4:%.*]] = integer_literal $Builtin.Int32, 10
+// CHECK: apply [[MARKER]]([[I4]])
+// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
+// CHECK: switch_value [[WHICH_YIELD]] : $Builtin.Int32, case [[ZERO]]: bb1
+
+// CHECK: bb5:
+// CHECK: [[I5:%.*]] = integer_literal $Builtin.Int32, 20
+// CHECK: %29 = apply [[MARKER]]([[I5]])
+// CHECK: br bb8
+
+// CHECK: bb6:
+// CHECK: [[I6:%.*]] = integer_literal $Builtin.Int32, 11
+// CHECK: apply [[MARKER]]([[I6]])
+// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
+// CHECK: switch_value [[WHICH_YIELD]] : $Builtin.Int32, case [[ZERO]]: bb2
+
+// CHECK: bb7:
+// CHECK: [[I7:%.*]] = integer_literal $Builtin.Int32, 21
+// CHECK: [[MARKER]]([[I7]])
+// CHECK: br bb8
+
+// CHECK:bb8:
+// CHECK: dealloc_stack [[IND_RES]] : $*Indirect<SomeSubclass>
+// CHECK: return
+// CHECK:}
+
+sil @test_simple_call : $(Builtin.Int1) -> () {
+entry(%flag : $Builtin.Int1):
+ %marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+ %0 = function_ref @test_one_yield : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @in Indirect<T>)
+ (%value, %token) = begin_apply %0<SomeSubclass>() : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @in Indirect<T>)
+ destroy_addr %value : $*Indirect<SomeSubclass>
+ cond_br %flag, yes, no
+
+yes:
+ %10 = integer_literal $Builtin.Int32, 10
+ apply %marker(%10) : $@convention(thin) (Builtin.Int32) -> ()
+ end_apply %token
+ %20 = integer_literal $Builtin.Int32, 20
+ apply %marker(%20) : $@convention(thin) (Builtin.Int32) -> ()
+ br cont
+
+no:
+ %11 = integer_literal $Builtin.Int32, 11
+ apply %marker(%11) : $@convention(thin) (Builtin.Int32) -> ()
+ abort_apply %token
+ %21 = integer_literal $Builtin.Int32, 21
+ apply %marker(%21) : $@convention(thin) (Builtin.Int32) -> ()
+ br cont
+
+cont:
+ %ret = tuple ()
+ return %ret : $()
+}
+
+sil [transparent] @test_two_yield : $@yield_once <C: SomeClass> (Builtin.Int1) -> (@yields @in Indirect<C>, @yields Builtin.Int64) {
+entry(%0 : $Builtin.Int1):
+ %marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+ %1000 = integer_literal $Builtin.Int32, 1000
+ apply %marker(%1000) : $@convention(thin) (Builtin.Int32) -> ()
+ %temp = alloc_stack $Indirect<C>
+ %make = function_ref @make_indirect : $@convention(thin) <T: SomeClass> () -> (@out Indirect<T>)
+ cond_br %0, yield1, yield2
+
+yield1:
+ apply %make<C>(%temp) : $@convention(thin) <T: SomeClass> () -> (@out Indirect<T>)
+ %res = integer_literal $Builtin.Int64, 31
+ yield (%temp : $*Indirect<C>, %res: $Builtin.Int64), resume resume1, unwind unwind1
+
+yield2:
+ apply %make<C>(%temp) : $@convention(thin) <T: SomeClass> () -> (@out Indirect<T>)
+ %res2 = integer_literal $Builtin.Int64, 32
+ yield (%temp : $*Indirect<C>, %res2: $Builtin.Int64), resume resume2, unwind unwind2
+
+resume1:
+ br resume
+resume2:
+ br resume
+
+resume:
+ %2000 = integer_literal $Builtin.Int32, 2000
+ apply %marker(%2000) : $@convention(thin) (Builtin.Int32) -> ()
+ dealloc_stack %temp : $*Indirect<C>
+ %ret = tuple ()
+ return %ret : $()
+
+unwind1:
+ br unwind
+unwind2:
+ br unwind
+
+unwind:
+ %3000 = integer_literal $Builtin.Int32, 3000
+ apply %marker(%3000) : $@convention(thin) (Builtin.Int32) -> ()
+ dealloc_stack %temp : $*Indirect<C>
+ unwind
+}
+
+// CHECK-LABEL: sil @test_simple_call_two_yields : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
+// CHECK: bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
+// CHECK: %2 = alloc_stack $Indirect<SomeSubclass>
+// CHECK: %3 = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+// CHECK: %4 = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+// CHECK: %5 = integer_literal $Builtin.Int32, 1000
+// CHECK: %6 = apply %4(%5) : $@convention(thin) (Builtin.Int32) -> ()
+// CHECK: %7 = alloc_stack $Indirect<SomeSubclass>
+// CHECK: %8 = function_ref @make_indirect
+// CHECK: cond_br %0, bb1, bb6
+
+// CHECK: bb1:
+// CHECK: %10 = apply %8<SomeSubclass>(%7)
+// CHECK: %11 = integer_literal $Builtin.Int64, 31
+// CHECK: %12 = integer_literal $Builtin.Int32, 0
+// CHECK: copy_addr [take] %7 to [initialization] %2 : $*Indirect<SomeSubclass>
+// CHECK: br bb9(%11 : $Builtin.Int64, %12 : $Builtin.Int32)
+
+// CHECK: bb2:
+// CHECK: br bb3
+
+// CHECK: bb3:
+// CHECK: %16 = integer_literal $Builtin.Int32, 2000
+// CHECK: %17 = apply %4(%16)
+// CHECK: dealloc_stack %7 : $*Indirect<SomeSubclass>
+// CHECK: br bb11
+
+// CHECK: bb4:
+// CHECK: br bb5
+
+// CHECK: bb5:
+// CHECK: %22 = integer_literal $Builtin.Int32, 3000
+// CHECK: %23 = apply %4(%22)
+// CHECK: dealloc_stack %7 : $*Indirect<SomeSubclass>
+// CHECK: br bb13
+
+// CHECK: bb6:
+// CHECK: %26 = apply %8<SomeSubclass>(%7) : $@convention(thin) <τ_0_0 where τ_0_0 : SomeClass> () -> @out Indirect<τ_0_0>
+// CHECK: %27 = integer_literal $Builtin.Int64, 32
+// CHECK: %28 = integer_literal $Builtin.Int32, 1
+// CHECK: copy_addr [take] %7 to [initialization] %2 : $*Indirect<SomeSubclass>
+// CHECK: br bb9(%27 : $Builtin.Int64, %28 : $Builtin.Int32)
+
+// CHECK: bb7:
+// CHECK: br bb3
+
+// CHECK: bb8:
+// CHECK: br bb5
+
+// CHECK: bb9(%33 : $Builtin.Int64, %34 : $Builtin.Int32):
+// CHECK: destroy_addr %2 : $*Indirect<SomeSubclass>
+// CHECK: cond_br %1, bb10, bb12
+
+// CHECK: bb10:
+// CHECK: %37 = integer_literal $Builtin.Int32, 0
+// CHECK: %38 = integer_literal $Builtin.Int32, 1
+// CHECK: switch_value %34 : $Builtin.Int32, case %37: bb2, case %38: bb7
+
+// CHECK: bb11:
+// CHECK: br bb14
+
+// CHECK: bb12:
+// CHECK: %41 = integer_literal $Builtin.Int32, 0
+// CHECK: %42 = integer_literal $Builtin.Int32, 1
+// CHECK: switch_value %34 : $Builtin.Int32, case %41: bb4, case %42: bb8
+
+// CHECK: bb13:
+// CHECK: br bb14
+
+// CHECK: bb14:
+// CHECK: %45 = tuple ()
+// CHECK: dealloc_stack %2 : $*Indirect<SomeSubclass>
+// CHECK: return %45 : $()
+
+sil @test_simple_call_two_yields : $(Builtin.Int1, Builtin.Int1) -> () {
+entry(%flag : $Builtin.Int1, %flag2: $Builtin.Int1):
+ %marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+ %0 = function_ref @test_two_yield : $@convention(thin) @yield_once <T: SomeClass> (Builtin.Int1) -> (@yields @in Indirect<T>, @yields Builtin.Int64)
+ (%value, %value2, %token) = begin_apply %0<SomeSubclass>(%flag) : $@convention(thin) @yield_once <T: SomeClass> (Builtin.Int1) -> (@yields @in Indirect<T>, @yields Builtin.Int64)
+ destroy_addr %value : $*Indirect<SomeSubclass>
+ cond_br %flag2, yes, no
+
+yes:
+ end_apply %token
+ br cont
+
+no:
+ abort_apply %token
+ br cont
+
+cont:
+ %ret = tuple ()
+ return %ret : $()
+}
+
+// CHECK: sil @test_simple_call_yield_owned : $@convention(thin) (Builtin.Int1, @owned SomeClass) -> () {
+// CHECK: bb0(%0 : $Builtin.Int1, %1 : $SomeClass):
+// CHECK: %2 = function_ref @marker
+// CHECK: %3 = integer_literal $Builtin.Int32, 1000
+// CHECK: %4 = apply %2(%3)
+// CHECK: %5 = integer_literal $Builtin.Int32, 0
+// CHECK: br bb3(%1 : $SomeClass, %5 : $Builtin.Int32)
+
+// CHECK: bb1:
+// CHECK: %7 = integer_literal $Builtin.Int32, 2000
+// CHECK: %8 = apply %2(%7)
+// CHECK: destroy_value %1 : $SomeClass
+// CHECK: br bb5
+
+// CHECK: bb2:
+// CHECK: %12 = integer_literal $Builtin.Int32, 3000
+// CHECK: %13 = apply %2(%12)
+// CHECK: destroy_value %1 : $SomeClass
+// CHECK: br bb7
+
+// CHECK: bb3(%16 : $SomeClass, %17 : $Builtin.Int32):
+// CHECK: cond_br %0, bb4, bb6
+
+// CHECK: bb4:
+// CHECK: %19 = integer_literal $Builtin.Int32, 0
+// CHECK: switch_value %17 : $Builtin.Int32, case %19: bb1
+
+// CHECK: bb5:
+// CHECK: br bb8
+
+// CHECK: bb6:
+// CHECK: %22 = integer_literal $Builtin.Int32, 0
+// CHECK: switch_value %17 : $Builtin.Int32, case %22: bb2
+
+// CHECK: bb7:
+// CHECK: br bb8
+
+// CHECK: bb8:
+// CHECK: %25 = tuple ()
+// CHECK: return %25 : $()
+
+sil [transparent] @yield_owned : $@yield_once(@owned SomeClass) -> (@yields @owned SomeClass) {
+entry(%0 : $SomeClass):
+ %marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
+ %1000 = integer_literal $Builtin.Int32, 1000
+ apply %marker(%1000) : $@convention(thin) (Builtin.Int32) -> ()
+ yield %0 : $SomeClass, resume resume, unwind unwind
+
+resume:
+ %2000 = integer_literal $Builtin.Int32, 2000
+ apply %marker(%2000) : $@convention(thin) (Builtin.Int32) -> ()
+ destroy_value %0: $SomeClass
+ %ret = tuple ()
+ return %ret : $()
+
+unwind:
+ %3000 = integer_literal $Builtin.Int32, 3000
+ apply %marker(%3000) : $@convention(thin) (Builtin.Int32) -> ()
+ destroy_value %0: $SomeClass
+ unwind
+}
+
+sil @test_simple_call_yield_owned : $(Builtin.Int1, @owned SomeClass) -> () {
+entry(%flag : $Builtin.Int1, %c: $SomeClass):
+ %0 = function_ref @yield_owned : $@convention(thin) @yield_once(@owned SomeClass) -> (@yields @owned SomeClass)
+ (%value, %token) = begin_apply %0(%c) : $@convention(thin) @yield_once(@owned SomeClass) -> (@yields @owned SomeClass)
+ cond_br %flag, yes, no
+
+yes:
+ end_apply %token
+ br cont
+
+no:
+ abort_apply %token
+ br cont
+
+cont:
+ %ret = tuple ()
+ return %ret : $()
+}