Update SIL devirtualization to handle begin_apply instructions.

In order to make this reasonable, I needed to shift responsibilities
around a little; the devirtualization operation is now responsible for
replacing uses of the original apply.  I wanted to remove the
phase-separation completely, but there was optimization-remark code
relying on the old apply site not having been deleted yet.

The begin_apply aspects of this aren't testable independently of
replacing materializeForSet because coroutines are currently never
called indirectly.
diff --git a/include/swift/SILOptimizer/Utils/Devirtualize.h b/include/swift/SILOptimizer/Utils/Devirtualize.h
index 9119be7..ee08fd8 100644
--- a/include/swift/SILOptimizer/Utils/Devirtualize.h
+++ b/include/swift/SILOptimizer/Utils/Devirtualize.h
@@ -35,22 +35,6 @@
 class Emitter;
 }
 
-/// A pair representing results of devirtualization.
-///  - The first element is the value representing the result of the
-///    devirtualized call.
-///  - The second element is the new apply/try_apply instruction.
-/// If no devirtualization was possible, the pair:
-/// <nullptr, FullApplySite()> is returned.
-///
-/// Two elements are required, because a result of the new devirtualized
-/// apply/try_apply instruction (second element) eventually needs to be
-/// casted to produce a properly typed value (first element).
-///
-/// *NOTE* The reason why we use a ValueBase here instead of a SILInstruction is
-/// that a devirtualization result may be a BB arg if there was a cast in
-/// between optional types.
-typedef std::pair<ValueBase *, ApplySite> DevirtualizationResult;
-
 /// Compute all subclasses of a given class.
 ///
 /// \p CHA class hierarchy analysis
@@ -64,9 +48,14 @@
                       SILModule &M,
                       ClassHierarchyAnalysis::ClassList &Subs);
 
-DevirtualizationResult tryDevirtualizeApply(ApplySite AI,
-                                            ClassHierarchyAnalysis *CHA,
-                                            OptRemark::Emitter *ORE = nullptr);
+/// Attempt to devirtualize the given apply site.  If this fails,
+/// the returned ApplySite will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+ApplySite tryDevirtualizeApply(ApplySite AI,
+                               ClassHierarchyAnalysis *CHA,
+                               OptRemark::Emitter *ORE = nullptr);
 bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
 bool isNominalTypeWithUnboundGenericParameters(SILType Ty, SILModule &M);
 bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType,
@@ -74,15 +63,45 @@
                                 bool isEffectivelyFinalMethod = false);
 SILFunction *getTargetClassMethod(SILModule &M, SILType ClassOrMetatypeType,
                                   MethodInst *MI);
-DevirtualizationResult devirtualizeClassMethod(FullApplySite AI,
-                                               SILValue ClassInstance,
-                                               OptRemark::Emitter *ORE);
-DevirtualizationResult
+
+/// Devirtualize the given apply site, which is known to be devirtualizable.
+///
+/// The caller must call deleteDevirtualizedApply on the original apply site.
+FullApplySite devirtualizeClassMethod(FullApplySite AI,
+                                      SILValue ClassInstance,
+                                      OptRemark::Emitter *ORE);
+
+/// Attempt to devirtualize the given apply site, which is known to be
+/// of a class method.  If this fails, the returned FullApplySite will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+FullApplySite
 tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
                            OptRemark::Emitter *ORE,
                            bool isEffectivelyFinalMethod = false);
-DevirtualizationResult
-tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
-}
+
+/// Attempt to devirtualize the given apply site, which is known to be
+/// of a witness method.  If this fails, the returned FullApplySite
+/// will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+ApplySite tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
+
+/// Delete a successfully-devirtualized apply site.  This must always be
+/// called after devirtualizing an apply; not only is it not semantically
+/// equivalent to leave the old apply in-place, but the SIL isn't necessary
+/// well-formed.
+///
+/// Devirtualization is responsible for replacing uses of the original
+/// apply site with uses of the new one.  The only thing this does is delete
+/// the instruction and any now-trivially-dead operands; it is separated
+/// from the actual devirtualization step only to apply the caller to log
+/// information about the original apply site.  This is probably not a
+/// good enough reason to complicate the API.
+void deleteDevirtualizedApply(ApplySite AI);
+
+} // end namespace swift
 
 #endif
diff --git a/include/swift/SILOptimizer/Utils/Local.h b/include/swift/SILOptimizer/Utils/Local.h
index 0d0835f..0037b6a 100644
--- a/include/swift/SILOptimizer/Utils/Local.h
+++ b/include/swift/SILOptimizer/Utils/Local.h
@@ -119,11 +119,6 @@
 /// after \p ABI and returns it.
 ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index);
 
-/// Replace an apply with an instruction that produces the same value,
-/// then delete the apply and the instructions that produce its callee
-/// if possible.
-void replaceDeadApply(ApplySite Old, ValueBase *New);
-
 /// \brief Return true if any call inside the given function may bind dynamic
 /// 'Self' to a generic argument of the callee.
 bool mayBindDynamicSelf(SILFunction *F);
diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp
index 9918dbc..abcf59a 100644
--- a/lib/SIL/SILVerifier.cpp
+++ b/lib/SIL/SILVerifier.cpp
@@ -4527,6 +4527,7 @@
     SILModule &M = F->getModule();
     for (auto &BB : *F) {
       TermInst *TI = BB.getTerminator();
+      CurInstruction = TI;
 
       // Check for non-cond_br critical edges in canonical SIL.
       if (!isa<CondBranchInst>(TI) && M.getStage() == SILStage::Canonical) {
diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
index c24852b..5a00f2e 100644
--- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
+++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
@@ -434,14 +434,14 @@
 static std::tuple<FullApplySite, SILBasicBlock::iterator>
 tryDevirtualizeApplyHelper(FullApplySite InnerAI, SILBasicBlock::iterator I,
                            ClassHierarchyAnalysis *CHA) {
-  auto NewInstPair = tryDevirtualizeApply(InnerAI, CHA);
-  if (!NewInstPair.second) {
+  auto NewInst = tryDevirtualizeApply(InnerAI, CHA);
+  if (!NewInst) {
     return std::make_tuple(InnerAI, I);
   }
 
-  replaceDeadApply(InnerAI, NewInstPair.first);
+  deleteDevirtualizedApply(InnerAI);
 
-  auto newApplyAI = NewInstPair.second.getInstruction();
+  auto newApplyAI = NewInst.getInstruction();
   assert(newApplyAI && "devirtualized but removed apply site?");
 
   return std::make_tuple(FullApplySite::isa(newApplyAI),
diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp
index f2fd84f..36ccbb8 100644
--- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp
+++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp
@@ -69,14 +69,14 @@
    }
   }
   for (auto Apply : Applies) {
-    auto NewInstPair = tryDevirtualizeApply(Apply, CHA, &ORE);
-    if (!NewInstPair.second)
+    auto NewInst = tryDevirtualizeApply(Apply, CHA, &ORE);
+    if (!NewInst)
       continue;
 
     Changed = true;
 
-    replaceDeadApply(Apply, NewInstPair.first);
-    NewApplies.push_back(NewInstPair.second);
+    deleteDevirtualizedApply(Apply);
+    NewApplies.push_back(NewInst);
   }
 
   // For each new apply, attempt to link in function bodies if we do
diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
index 88fadb2..6205f1b 100644
--- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
+++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
@@ -29,6 +29,7 @@
 #include "swift/SILOptimizer/PassManager/Passes.h"
 #include "swift/SILOptimizer/PassManager/PassManager.h"
 #include "swift/SILOptimizer/PassManager/Transforms.h"
+#include "swift/SILOptimizer/Utils/CFG.h"
 #include "swift/SILOptimizer/Utils/Devirtualize.h"
 #include "swift/SILOptimizer/Utils/SILInliner.h"
 #include "swift/AST/ASTContext.h"
@@ -47,6 +48,38 @@
 
 STATISTIC(NumTargetsPredicted, "Number of monomorphic functions predicted");
 
+/// We want to form a second edge to the given block, but we know
+/// that'll form a critical edge.  Return a basic block to which we can
+/// create an edge essentially like the original edge.
+static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) {
+#ifndef NDEBUG
+  auto origDestBB = TI->getSuccessors()[SuccIndex].getBB();
+#endif
+
+  // Split the edge twice.  The first split will become our cloned
+  // and temporarily-unused edge.  The second split will remain in place
+  // as the original edge.
+  auto clonedEdgeBB = splitEdge(TI, SuccIndex);
+  auto replacementEdgeBB = splitEdge(TI, SuccIndex);
+
+  // Extract the terminators.
+  auto clonedEdgeBranch =
+    cast<BranchInst>(clonedEdgeBB->getTerminator());
+  auto replacementEdgeBranch =
+    cast<BranchInst>(replacementEdgeBB->getTerminator());
+
+  assert(TI->getSuccessors()[SuccIndex].getBB() == replacementEdgeBB);
+  assert(replacementEdgeBranch->getDestBB() == clonedEdgeBB);
+  assert(clonedEdgeBranch->getDestBB() == origDestBB);
+
+  // Change the replacement branch to point to the original destination.
+  // This will leave the cloned edge unused, which is how we wanted it.
+  replacementEdgeBranch->getSuccessors()[0] = clonedEdgeBranch->getDestBB();
+  assert(clonedEdgeBB->pred_empty());
+
+  return clonedEdgeBB;
+}
+
 // A utility function for cloning the apply instruction.
 static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) {
   // Clone the Apply.
@@ -68,18 +101,17 @@
     break;
   case SILInstructionKind::TryApplyInst: {
     auto *TryApplyI = cast<TryApplyInst>(AI.getInstruction());
+    auto NormalBB = cloneEdge(TryApplyI, TryApplyInst::NormalIdx);
+    auto ErrorBB = cloneEdge(TryApplyI, TryApplyInst::ErrorIdx);
     NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(),
                                  AI.getSubstitutionMap(),
-                                 Ret,
-                                 TryApplyI->getNormalBB(),
-                                 TryApplyI->getErrorBB());
-    }
+                                 Ret, NormalBB, ErrorBB);
     break;
+  }
   default:
     llvm_unreachable("Trying to clone an unsupported apply instruction");
   }
 
-  NAI.getInstruction();
   return NAI;
 }
 
@@ -96,6 +128,10 @@
   if (SubType.getASTType()->hasDynamicSelfType())
     return FullApplySite();
 
+  // Can't speculate begin_apply yet.
+  if (isa<BeginApplyInst>(AI))
+    return FullApplySite();
+
   // Create a diamond shaped control flow and a checked_cast_branch
   // instruction that checks the exact type of the object.
   // This cast selects between two paths: one that calls the slow dynamic
@@ -182,10 +218,10 @@
   NumTargetsPredicted++;
 
   // Devirtualize the apply instruction on the identical path.
-  auto NewInstPair =
+  auto NewInst =
       devirtualizeClassMethod(IdenAI, DownCastedClassInstance, nullptr);
-  assert(NewInstPair.first && "Expected to be able to devirtualize apply!");
-  replaceDeadApply(IdenAI, NewInstPair.first);
+  assert(NewInst && "Expected to be able to devirtualize apply!");
+  deleteDevirtualizedApply(IdenAI);
 
   // Split critical edges resulting from VirtAI.
   if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) {
@@ -366,10 +402,10 @@
     // try to devirtualize it completely.
     ClassHierarchyAnalysis::ClassList Subs;
     if (isDefaultCaseKnown(CHA, AI, CD, Subs)) {
-      auto NewInstPair = tryDevirtualizeClassMethod(AI, SubTypeValue, &ORE);
-      if (NewInstPair.first)
-        replaceDeadApply(AI, NewInstPair.first);
-      return NewInstPair.second.getInstruction() != nullptr;
+      auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, &ORE);
+      if (NewInst)
+        deleteDevirtualizedApply(AI);
+      return bool(NewInst);
     }
 
     LLVM_DEBUG(llvm::dbgs() << "Inserting monomorphic speculative call for "
@@ -529,10 +565,10 @@
     ORE.emit(RB);
     return true;
   }
-  auto NewInstPair = tryDevirtualizeClassMethod(AI, SubTypeValue, nullptr);
-  if (NewInstPair.first) {
+  auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, nullptr);
+  if (NewInst) {
     ORE.emit(RB);
-    replaceDeadApply(AI, NewInstPair.first);
+    deleteDevirtualizedApply(AI);
     return true;
   }
 
diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp
index 4410b59..5683fd5 100644
--- a/lib/SILOptimizer/Utils/Devirtualize.cpp
+++ b/lib/SILOptimizer/Utils/Devirtualize.cpp
@@ -466,6 +466,140 @@
                                              baseCalleeSig);
 }
 
+static ApplyInst *replaceApplyInst(SILBuilder &B, SILLocation Loc,
+                                   ApplyInst *OldAI,
+                                   SILValue NewFn,
+                                   SubstitutionMap NewSubs,
+                                   ArrayRef<SILValue> NewArgs) {
+  auto *NewAI = B.createApply(Loc, NewFn, NewSubs, NewArgs,
+                              OldAI->isNonThrowing());
+
+  // Check if any casting is required for the return value.
+  SILValue ResultValue =
+    castValueToABICompatibleType(&B, Loc, NewAI, NewAI->getType(),
+                                 OldAI->getType());
+
+  OldAI->replaceAllUsesWith(ResultValue);
+  return NewAI;
+}
+
+static TryApplyInst *replaceTryApplyInst(SILBuilder &B, SILLocation Loc,
+                                         TryApplyInst *OldTAI,
+                                         SILValue NewFn,
+                                         SubstitutionMap NewSubs,
+                                         ArrayRef<SILValue> NewArgs,
+                                         SILFunctionConventions Conv) {
+  SILBasicBlock *NormalBB = OldTAI->getNormalBB();
+  SILBasicBlock *ResultBB = nullptr;
+
+  SILType NewResultTy = Conv.getSILResultType();
+
+  // Does the result value need to be casted?
+  auto OldResultTy = NormalBB->getArgument(0)->getType();
+  bool ResultCastRequired = NewResultTy != OldResultTy;
+
+  // Create a new normal BB only if the result of the new apply differs
+  // in type from the argument of the original normal BB.
+  if (!ResultCastRequired) {
+    ResultBB = NormalBB;
+  } else {
+    ResultBB = B.getFunction().createBasicBlockBefore(NormalBB);
+    ResultBB->createPHIArgument(NewResultTy, ValueOwnershipKind::Owned);
+  }
+
+  // We can always just use the original error BB because we'll be
+  // deleting the edge to it from the old TAI.
+  SILBasicBlock *ErrorBB = OldTAI->getErrorBB();
+
+  // Insert a try_apply here.
+  // Note that this makes this block temporarily double-terminated!
+  // We won't fix that until deleteDevirtualizedApply.
+  auto NewTAI = B.createTryApply(Loc, NewFn, NewSubs, NewArgs,
+                                 ResultBB, ErrorBB);
+
+  if (ResultCastRequired) {
+    B.setInsertionPoint(ResultBB);
+
+    SILValue ResultValue = ResultBB->getArgument(0);
+    ResultValue = castValueToABICompatibleType(&B, Loc, ResultValue,
+                                               NewResultTy, OldResultTy);
+
+    B.createBranch(Loc, NormalBB, { ResultValue });
+  }
+
+  B.setInsertionPoint(NormalBB->begin());
+  return NewTAI;
+}
+
+static BeginApplyInst *replaceBeginApplyInst(SILBuilder &B, SILLocation Loc,
+                                             BeginApplyInst *OldBAI,
+                                             SILValue NewFn,
+                                             SubstitutionMap NewSubs,
+                                             ArrayRef<SILValue> NewArgs) {
+  auto NewBAI = B.createBeginApply(Loc, NewFn, NewSubs, NewArgs,
+                                   OldBAI->isNonThrowing());
+
+  // Forward the token.
+  OldBAI->getTokenResult()->replaceAllUsesWith(NewBAI->getTokenResult());
+
+  auto OldYields = OldBAI->getYieldedValues();
+  auto NewYields = NewBAI->getYieldedValues();
+  assert(OldYields.size() == NewYields.size());
+
+  for (auto i : indices(OldYields)) {
+    auto OldYield = OldYields[i];
+    auto NewYield = NewYields[i];
+    NewYield = castValueToABICompatibleType(&B, Loc, NewYield,
+                                            NewYield->getType(),
+                                            OldYield->getType());
+    OldYield->replaceAllUsesWith(NewYield);
+  }
+
+  return NewBAI;
+}
+
+static PartialApplyInst *replacePartialApplyInst(SILBuilder &B, SILLocation Loc,
+                                                 PartialApplyInst *OldPAI,
+                                                 SILValue NewFn,
+                                                 SubstitutionMap NewSubs,
+                                                 ArrayRef<SILValue> NewArgs) {
+  auto Convention =
+    OldPAI->getType().getAs<SILFunctionType>()->getCalleeConvention();
+  auto *NewPAI = B.createPartialApply(Loc, NewFn, NewSubs, NewArgs,
+                                      Convention);
+
+  // Check if any casting is required for the partially-applied function.
+  SILValue ResultValue = castValueToABICompatibleType(
+      &B, Loc, NewPAI, NewPAI->getType(), OldPAI->getType());
+  OldPAI->replaceAllUsesWith(ResultValue);
+
+  return NewPAI;
+}
+
+static ApplySite replaceApplySite(SILBuilder &B, SILLocation Loc,
+                                  ApplySite OldAS,
+                                  SILValue NewFn,
+                                  SubstitutionMap NewSubs,
+                                  ArrayRef<SILValue> NewArgs,
+                                  SILFunctionConventions Conv) {
+  if (auto *OldAI = dyn_cast<ApplyInst>(OldAS)) {
+    return replaceApplyInst(B, Loc, OldAI, NewFn, NewSubs, NewArgs);
+  } else if (auto *OldTAI = dyn_cast<TryApplyInst>(OldAS)) {
+    return replaceTryApplyInst(B, Loc, OldTAI, NewFn, NewSubs, NewArgs, Conv);
+  } else if (auto *OldBAI = dyn_cast<BeginApplyInst>(OldAS)) {
+    return replaceBeginApplyInst(B, Loc, OldBAI, NewFn, NewSubs, NewArgs);
+  } else {
+    auto *OldPAI = cast<PartialApplyInst>(OldAS);
+    return replacePartialApplyInst(B, Loc, OldPAI, NewFn, NewSubs, NewArgs);
+  }
+}
+
+/// Delete an apply site that's been successfully devirtualized.
+void swift::deleteDevirtualizedApply(ApplySite Old) {
+  auto *OldApply = Old.getInstruction();
+  recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
+}
+
 SILFunction *swift::getTargetClassMethod(SILModule &M,
                                          SILType ClassOrMetatypeType,
                                          MethodInst *MI) {
@@ -551,9 +685,9 @@
 /// \p ClassOrMetatype is a class value or metatype value that is the
 ///    self argument of the apply we will devirtualize.
 /// return the result value of the new ApplyInst if created one or null.
-DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
-                                                      SILValue ClassOrMetatype,
-                                                      OptRemark::Emitter *ORE) {
+FullApplySite swift::devirtualizeClassMethod(FullApplySite AI,
+                                             SILValue ClassOrMetatype,
+                                             OptRemark::Emitter *ORE) {
   LLVM_DEBUG(llvm::dbgs() << "    Trying to devirtualize : "
                           << *AI.getInstruction());
 
@@ -574,7 +708,8 @@
   SILFunctionConventions substConv(SubstCalleeType, Mod);
 
   SILBuilderWithScope B(AI.getInstruction());
-  FunctionRefInst *FRI = B.createFunctionRef(AI.getLoc(), F);
+  SILLocation Loc = AI.getLoc();
+  FunctionRefInst *FRI = B.createFunctionRef(Loc, F);
 
   // Create the argument list for the new apply, casting when needed
   // in order to handle covariant indirect return types and
@@ -584,7 +719,7 @@
   auto IndirectResultArgIter = AI.getIndirectSILResults().begin();
   for (auto ResultTy : substConv.getIndirectSILResultTypes()) {
     NewArgs.push_back(
-        castValueToABICompatibleType(&B, AI.getLoc(), *IndirectResultArgIter,
+        castValueToABICompatibleType(&B, Loc, *IndirectResultArgIter,
                                      IndirectResultArgIter->getType(), ResultTy));
     ++IndirectResultArgIter;
   }
@@ -594,7 +729,7 @@
   for (auto param : substConv.getParameters().drop_back()) {
     auto paramType = substConv.getSILType(param);
     NewArgs.push_back(
-        castValueToABICompatibleType(&B, AI.getLoc(), *ParamArgIter,
+        castValueToABICompatibleType(&B, Loc, *ParamArgIter,
                                      ParamArgIter->getType(), paramType));
     ++ParamArgIter;
   }
@@ -602,80 +737,14 @@
   // Add the self argument, upcasting if required because we're
   // calling a base class's method.
   auto SelfParamTy = substConv.getSILType(SubstCalleeType->getSelfParameter());
-  NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(),
+  NewArgs.push_back(castValueToABICompatibleType(&B, Loc,
                                                  ClassOrMetatype,
                                                  ClassOrMetatypeType,
                                                  SelfParamTy));
 
-  SILType ResultTy = substConv.getSILResultType();
-
-  FullApplySite NewAI;
-
-  SILBasicBlock *ResultBB = nullptr;
-  SILBasicBlock *NormalBB = nullptr;
-  SILValue ResultValue;
-  bool ResultCastRequired = false;
-  SmallVector<Operand *, 4> OriginalResultUses;
-
-  if (!isa<TryApplyInst>(AI)) {
-    auto apply = B.createApply(AI.getLoc(), FRI, Subs, NewArgs,
-                               cast<ApplyInst>(AI)->isNonThrowing());
-    NewAI = apply;
-    ResultValue = apply;
-  } else {
-    auto *TAI = cast<TryApplyInst>(AI);
-    // Create new normal and error BBs only if:
-    // - re-using a BB would create a critical edge
-    // - or, the result of the new apply would be of different
-    //   type than the argument of the original normal BB.
-    if (TAI->getNormalBB()->getSinglePredecessorBlock())
-      ResultBB = TAI->getNormalBB();
-    else {
-      ResultBB = B.getFunction().createBasicBlock();
-      ResultBB->createPHIArgument(ResultTy, ValueOwnershipKind::Owned);
-    }
-
-    NormalBB = TAI->getNormalBB();
-
-    SILBasicBlock *ErrorBB = nullptr;
-    if (TAI->getErrorBB()->getSinglePredecessorBlock())
-      ErrorBB = TAI->getErrorBB();
-    else {
-      ErrorBB = B.getFunction().createBasicBlock();
-      ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(),
-                                 ValueOwnershipKind::Owned);
-    }
-
-    NewAI = B.createTryApply(AI.getLoc(), FRI, Subs, NewArgs, ResultBB, ErrorBB);
-    if (ErrorBB != TAI->getErrorBB()) {
-      B.setInsertionPoint(ErrorBB);
-      B.createBranch(TAI->getLoc(), TAI->getErrorBB(),
-                     {ErrorBB->getArgument(0)});
-    }
-
-    // Does the result value need to be casted?
-    ResultCastRequired = ResultTy != NormalBB->getArgument(0)->getType();
-
-    if (ResultBB != NormalBB)
-      B.setInsertionPoint(ResultBB);
-    else if (ResultCastRequired) {
-      B.setInsertionPoint(NormalBB->begin());
-      // Collect all uses, before casting.
-      for (auto *Use : NormalBB->getArgument(0)->getUses()) {
-        OriginalResultUses.push_back(Use);
-      }
-      NormalBB->getArgument(0)->replaceAllUsesWith(
-          SILUndef::get(AI.getType(), Mod));
-      NormalBB->replacePHIArgument(0, ResultTy, ValueOwnershipKind::Owned);
-    }
-
-    // The result value is passed as a parameter to the normal block.
-    ResultValue = ResultBB->getArgument(0);
-  }
-
-  // Check if any casting is required for the return value.
-  ResultValue = castValueToABICompatibleType(&B, NewAI.getLoc(), ResultValue,
-                                             ResultTy, AI.getType());
+  ApplySite NewAS = replaceApplySite(B, Loc, AI, FRI, Subs, NewArgs, substConv);
+  FullApplySite NewAI = FullApplySite::isa(NewAS.getInstruction());
+  assert(NewAI);
 
   LLVM_DEBUG(llvm::dbgs() << "        SUCCESS: " << F->getName() << "\n");
   if (ORE)
@@ -686,34 +755,16 @@
       });
   NumClassDevirt++;
 
-  if (NormalBB) {
-    if (NormalBB != ResultBB) {
-      // If artificial normal BB was introduced, branch
-      // to the original normal BB.
-      B.createBranch(NewAI.getLoc(), NormalBB, { ResultValue });
-    } else if (ResultCastRequired) {
-      // Update all original uses by the new value.
-      for (auto *Use: OriginalResultUses) {
-        Use->set(ResultValue);
-      }
-    }
-  }
-
-  // We need to return a pair of values here:
-  // - the first one is the actual result of the devirtualized call, possibly
-  //   casted into an appropriate type. This SILValue may be a BB arg, if it
-  //   was a cast between optional types.
-  // - the second one is the new apply site.
-  return std::make_pair(ResultValue, NewAI);
+  return NewAI;
 }
 
-DevirtualizationResult
+FullApplySite
 swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
                                   OptRemark::Emitter *ORE,
                                   bool isEffectivelyFinalMethod) {
   if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE,
                                   isEffectivelyFinalMethod))
-    return std::make_pair(nullptr, FullApplySite());
+    return FullApplySite();
   return devirtualizeClassMethod(AI, ClassInstance, ORE);
 }
 
@@ -855,7 +906,7 @@
 /// Generate a new apply of a function_ref to replace an apply of a
 /// witness_method when we've determined the actual function we'll end
 /// up calling.
-static DevirtualizationResult
+static ApplySite
 devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
                           ProtocolConformanceRef C, OptRemark::Emitter *ORE) {
   // We know the witness thunk and the corresponding set of substitutions
@@ -897,30 +948,8 @@
   SILLocation Loc = AI.getLoc();
   FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F);
 
-  ApplySite SAI;
-
-  SILValue ResultValue;
-  if (auto *A = dyn_cast<ApplyInst>(AI)) {
-    auto *NewAI = Builder.createApply(Loc, FRI, SubMap, Arguments,
-                                      A->isNonThrowing());
-    // Check if any casting is required for the return value.
-    ResultValue = castValueToABICompatibleType(&Builder, Loc, NewAI,
-                                               NewAI->getType(), AI.getType());
-    SAI = NewAI;
-  }
-  if (auto *TAI = dyn_cast<TryApplyInst>(AI))
-    SAI = Builder.createTryApply(Loc, FRI, SubMap, Arguments,
-                                 TAI->getNormalBB(), TAI->getErrorBB());
-  if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) {
-    auto PartialApplyConvention = PAI->getType().getAs<SILFunctionType>()
-                                      ->getCalleeConvention();
-    auto *NewPAI = Builder.createPartialApply(
-        Loc, FRI, SubMap, Arguments, PartialApplyConvention);
-    // Check if any casting is required for the return value.
-    ResultValue = castValueToABICompatibleType(
-        &Builder, Loc, NewPAI, NewPAI->getType(), PAI->getType());
-    SAI = NewPAI;
-  }
+  ApplySite SAI = replaceApplySite(Builder, Loc, AI, FRI, SubMap, Arguments,
+                                   substConv);
 
   if (ORE)
     ORE->emit([&]() {
@@ -929,7 +958,7 @@
                << "Devirtualized call to " << NV("Method", F);
       });
   NumWitnessDevirt++;
-  return std::make_pair(ResultValue, SAI);
+  return SAI;
 }
 
 static bool canDevirtualizeWitnessMethod(ApplySite AI) {
@@ -958,10 +987,10 @@
 /// In the cases where we can statically determine the function that
 /// we'll call to, replace an apply of a witness_method with an apply
 /// of a function_ref, returning the new apply.
-DevirtualizationResult
+ApplySite
 swift::tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE) {
   if (!canDevirtualizeWitnessMethod(AI))
-    return std::make_pair(nullptr, FullApplySite());
+    return ApplySite();
 
   SILFunction *F;
   SILWitnessTable *WT;
@@ -981,9 +1010,9 @@
 
 /// Attempt to devirtualize the given apply if possible, and return a
 /// new instruction in that case, or nullptr otherwise.
-DevirtualizationResult swift::tryDevirtualizeApply(ApplySite AI,
-                                                   ClassHierarchyAnalysis *CHA,
-                                                   OptRemark::Emitter *ORE) {
+ApplySite swift::tryDevirtualizeApply(ApplySite AI,
+                                      ClassHierarchyAnalysis *CHA,
+                                      OptRemark::Emitter *ORE) {
   LLVM_DEBUG(llvm::dbgs() << "    Trying to devirtualize: "
                           << *AI.getInstruction());
 
@@ -998,7 +1027,7 @@
   // TODO: check if we can also de-virtualize partial applies of class methods.
   FullApplySite FAS = FullApplySite::isa(AI.getInstruction());
   if (!FAS)
-    return std::make_pair(nullptr, ApplySite());
+    return ApplySite();
 
   /// Optimize a class_method and alloc_ref pair into a direct function
   /// reference:
@@ -1053,7 +1082,7 @@
     return tryDevirtualizeClassMethod(FAS, FAS.getArguments().back(), ORE);
   }
 
-  return std::make_pair(nullptr, ApplySite());
+  return ApplySite();
 }
 
 bool swift::canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA) {
diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp
index b01b514..cfda0a9 100644
--- a/lib/SILOptimizer/Utils/Local.cpp
+++ b/lib/SILOptimizer/Utils/Local.cpp
@@ -313,15 +313,6 @@
   return FullApplySite();
 }
 
-// Replace a dead apply with a new instruction that computes the same
-// value, and delete the old apply.
-void swift::replaceDeadApply(ApplySite Old, ValueBase *New) {
-  auto *OldApply = Old.getInstruction();
-  if (!isa<TryApplyInst>(OldApply))
-    cast<SingleValueInstruction>(OldApply)->replaceAllUsesWith(New);
-  recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
-}
-
 bool swift::mayBindDynamicSelf(SILFunction *F) {
   if (!F->hasSelfMetadataParam())
     return false;