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 : $()
+}