//===------------- Outliner.cpp - Outlining Transformations ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-outliner"

#include "swift/AST/ASTMangler.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/ManglingMacros.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;

llvm::cl::opt<std::string> DumpFuncsBeforeOutliner(
    "sil-dump-functions-before-outliner", llvm::cl::init(""),
    llvm::cl::desc(
        "Break before running each function pass on a particular function"));

namespace {

class OutlinerMangler : public Mangle::ASTMangler {
  /// The kind of method bridged.
  enum MethodKind : unsigned {
    BridgedProperty,
    BridgedProperty_Consuming,
    BridgedPropertyAddress,
    BridgedMethod,
  };

  llvm::BitVector *IsParameterBridged;
  llvm::BitVector *IsParameterGuaranteed;
  SILDeclRef MethodDecl;
  MethodKind Kind;
  bool IsReturnBridged;

public:
  /// Create an mangler for an outlined bridged method.
  OutlinerMangler(SILDeclRef Method, llvm::BitVector *ParameterBridged,
                  llvm::BitVector *IsParameterGuaranteed, bool ReturnBridged)
      : ASTMangler(Method.getASTContext()), IsParameterBridged(ParameterBridged),
        IsParameterGuaranteed(IsParameterGuaranteed), MethodDecl(Method),
        Kind(BridgedMethod), IsReturnBridged(ReturnBridged) {}

  /// Create an mangler for an outlined bridged property.
  OutlinerMangler(SILDeclRef Method, bool IsAddress, bool ConsumesValue)
      : ASTMangler(Method.getASTContext()), IsParameterBridged(nullptr), IsParameterGuaranteed(nullptr),
        MethodDecl(Method),
        Kind(IsAddress ? BridgedPropertyAddress
                       : (ConsumesValue ? BridgedProperty_Consuming
                                        : BridgedProperty)),
        IsReturnBridged(true) {}

  std::string mangle();

private:
  char getMethodKindMangling() {
    switch (Kind) {
    case BridgedProperty:
      return 'p';
    case BridgedProperty_Consuming:
      return 'o';
    case BridgedPropertyAddress:
      return 'a';
    case BridgedMethod:
      return 'm';
    }
    llvm_unreachable("unhandled kind");
  }
};
} // end anonymous namespace.

std::string OutlinerMangler::mangle() {
  beginManglingWithoutPrefix();

  appendOperator(MethodDecl.mangle());

  llvm::SmallString<128> Buffer;
  llvm::raw_svector_ostream Out(Buffer);

  Out << getMethodKindMangling();
  if (IsParameterBridged) {
    for (unsigned Idx = 0, E = IsParameterBridged->size(); Idx != E; ++Idx) {
      Out << (IsParameterBridged->test(Idx) ? 'b' : 'n');
      // NOTE: We must keep owned as having nothing here to preserve ABI since
      // mangling is part of ABI.
      Out << (IsParameterGuaranteed->test(Idx) ? "g" : "");
    }
  }
  Out << (IsReturnBridged ? 'b' : 'n');
  Out << '_';

  appendOperator("Te", Buffer);
  return finalize();
}

namespace {

class OutlinePattern {
protected:
  SILOptFunctionBuilder &FuncBuilder;
  InstModCallbacks callbacks;
  DeadEndBlocks *deBlocks;

public:
  OutlinePattern(SILOptFunctionBuilder &FuncBuilder,
                 InstModCallbacks callbacks,
                 DeadEndBlocks *deBlocks)
      : FuncBuilder(FuncBuilder), callbacks(callbacks), deBlocks(deBlocks) {}

  /// Match the instruction sequence.
  virtual bool matchInstSequence(SILBasicBlock::iterator I) = 0;

  /// Outline the matched instruction sequence.
  ///
  /// If a new outlined function is created return the function. If the outlined
  /// function already existed return null.
  /// Returns the last instruction of the matched sequence after the
  /// replacement.
  virtual std::pair<SILFunction *, SILBasicBlock::iterator>
  outline(SILModule &M) = 0;

  virtual std::string getOutlinedFunctionName() = 0;

  virtual ~OutlinePattern() {}
};

/// Get the bridgeToObjectiveC witness for the type.
static SILDeclRef getBridgeToObjectiveC(CanType NativeType) {
  auto &Ctx = NativeType->getASTContext();
  auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
  if (!Proto)
    return SILDeclRef();
  auto ConformanceRef = lookupConformance(NativeType, Proto);
  if (ConformanceRef.isInvalid())
    return SILDeclRef();

  auto Conformance = ConformanceRef.getConcrete();
  // bridgeToObjectiveC
  DeclName Name(Ctx, Ctx.Id_bridgeToObjectiveC, llvm::ArrayRef<Identifier>());
  auto *Requirement = dyn_cast_or_null<FuncDecl>(
    Proto->getSingleRequirement(Name));
  if (!Requirement)
    return SILDeclRef();

  auto Witness = Conformance->getWitnessDecl(Requirement);
  return SILDeclRef(Witness);
}

/// Get the _unconditionallyBridgeFromObjectiveC witness for the type.
SILDeclRef getBridgeFromObjectiveC(CanType NativeType) {
  auto &Ctx = NativeType->getASTContext();
  auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
  if (!Proto)
    return SILDeclRef();
  auto ConformanceRef = lookupConformance(NativeType, Proto);
  if (ConformanceRef.isInvalid())
    return SILDeclRef();
  auto Conformance = ConformanceRef.getConcrete();
  // _unconditionallyBridgeFromObjectiveC
  DeclName Name(Ctx, Ctx.getIdentifier("_unconditionallyBridgeFromObjectiveC"),
                llvm::ArrayRef(Identifier()));
  auto *Requirement = dyn_cast_or_null<FuncDecl>(
      Proto->getSingleRequirement(Name));
  if (!Requirement)
    return SILDeclRef();

  auto Witness = Conformance->getWitnessDecl(Requirement);
  return SILDeclRef(Witness);
}

struct SwitchInfo {
  SwitchEnumInst *SwitchEnum = nullptr;
  SILBasicBlock *SomeBB = nullptr;
  SILBasicBlock *NoneBB = nullptr;
  BranchInst *Br = nullptr;
};

/// Pattern for a bridged property call.
///
///  bb7:
///    %30 = unchecked_take_enum_data_addr %19 : $*Optional<UITextField>, #Optional.some!enumelt
///    %31 = load %30 : $*UITextField
///    strong_retain %31 : $UITextField
///    %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
///    %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
///    switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
///
///  bb8(%36 : $NSString):
///    // function_ref static String._unconditionallyBridgeFromObjectiveC(_:)
///    %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
///    %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
///    %39 = metatype $@thin String.Type
///    %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
///    %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
///    br bb10(%41 : $Optional<String>)
///
///  bb9:
///    %43 = enum $Optional<String>, #Optional.none!enumelt
///    br bb10(%43 : $Optional<String>)
///
///  bb10(%45 : $Optional<String>):
class BridgedProperty : public OutlinePattern {
  std::string OutlinedName;
  SingleValueInstruction *FirstInst; // A load or class_method
  SILBasicBlock *StartBB;
  SwitchInfo switchInfo;
  ObjCMethodInst *ObjCMethod;
  SILInstruction *Release;
  ApplyInst *PropApply;
  SILInstruction *UnpairedRelease; // A release_value | destroy_value following
                                   // the apply which isn't paired to
                                   // load [copy] | strong_retain first value.

public:
  bool matchInstSequence(SILBasicBlock::iterator I) override;

  std::pair<SILFunction *, SILBasicBlock::iterator>
  outline(SILModule &M) override;

  BridgedProperty(SILOptFunctionBuilder &FuncBuilder,
                  InstModCallbacks callbacks,
                  DeadEndBlocks *deBlocks)
      : OutlinePattern(FuncBuilder, callbacks, deBlocks) {
    clearState();
  }

  BridgedProperty(const BridgedProperty&) = delete;
  BridgedProperty& operator=(const BridgedProperty&) = delete;

  virtual ~BridgedProperty() {}

  std::string getOutlinedFunctionName() override;

private:
  bool matchMethodCall(SILBasicBlock::iterator, LoadInst *);
  CanSILFunctionType getOutlinedFunctionType(SILModule &M);
  void clearState();
};
}

void BridgedProperty::clearState() {
    FirstInst = nullptr;
    StartBB = nullptr;
    switchInfo = SwitchInfo();
    ObjCMethod = nullptr;
    Release = nullptr;
    PropApply = nullptr;
    UnpairedRelease = nullptr;
    OutlinedName.clear();
}

std::string BridgedProperty::getOutlinedFunctionName() {
  if (OutlinedName.empty()) {
    OutlinerMangler Mangler(ObjCMethod->getMember(), isa<LoadInst>(FirstInst),
                            UnpairedRelease);
    OutlinedName = Mangler.mangle();
  }
  return OutlinedName;
}

/// Returns the outlined function type.
///
/// This depends on the first instruction we matched. Either we matched a load
/// or we started the match at the class method instruction.
///
/// load %30 : *UITextField:
///   (@in_guaranteed InstanceType) -> (@owned Optional<BridgedInstanceType>)
/// objc_method %31 : UITextField
///   (@unowned InstanceType) -> (@owned Optional<BridgedInstanceType>)
///
CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) {
  SmallVector<SILParameterInfo, 4> Parameters;
  if (auto *Load = dyn_cast<LoadInst>(FirstInst))
    Parameters.push_back(
      SILParameterInfo(Load->getType().getASTType(),
                       ParameterConvention::Indirect_In_Guaranteed));
  else
    Parameters.push_back(SILParameterInfo(
        cast<ObjCMethodInst>(FirstInst)->getOperand()->getType().getASTType(),
        UnpairedRelease ? ParameterConvention::Direct_Owned
                        : ParameterConvention::Direct_Unowned));
  SmallVector<SILResultInfo, 4> Results;

  Results.push_back(SILResultInfo(
                      switchInfo.Br->getArg(0)->getType().getASTType(),
                      ResultConvention::Owned));
  auto ExtInfo = SILFunctionType::ExtInfo::getThin();
  auto FunctionType = SILFunctionType::get(
      nullptr, ExtInfo, SILCoroutineKind::None,
      ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, Results,
      std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext());
  return FunctionType;
}

static void eraseBlock(SILBasicBlock *block) {
  for (SILInstruction &inst : *block) {
    inst.replaceAllUsesOfAllResultsWithUndef();
  }
  block->eraseFromParent();
}

std::pair<SILFunction *, SILBasicBlock::iterator>
BridgedProperty::outline(SILModule &M) {
  // Get the function type.
  auto FunctionType = getOutlinedFunctionType(M);

  std::string nameTmp = getOutlinedFunctionName();
  auto name = M.allocateCopy(nameTmp);

  auto *Fun = FuncBuilder.getOrCreateFunction(
      ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare,
      IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed,
      IsNotRuntimeAccessible);
  bool NeedsDefinition = Fun->empty();

  if (Release) {
    // Move the release after the call.
    Release->moveBefore(StartBB->getTerminator());
  }

  //     [StartBB]
  //    /        \
  // [NoneBB]  [SomeBB]
  //   \          /
  //   [OldMergeBB]
  //
  //   Split to:
  //
  //      [StartBB]
  //          |
  //   [OutlinedEntryBB]   }
  //    /        \         }
  // [NoneBB]  [SomeBB]    } outlined
  //   \          /        }
  //   [OldMergeBB]        }
  //       |
  //   [NewTailBB]
  //
  auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(FirstInst));
  auto *OldMergeBB = switchInfo.Br->getDestBB();
  auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin());
  if (deBlocks) {
    deBlocks->updateForNewBlock(NewTailBB);
  }
  // Call the outlined function.
  {
    SILBuilder Builder(StartBB);
    auto Loc = FirstInst->getLoc();
    SILValue FunRef(Builder.createFunctionRef(Loc, Fun));
    SILValue Apply(
        Builder.createApply(Loc, FunRef, SubstitutionMap(),
                            {FirstInst->getOperand(0)}));
    Builder.createBranch(Loc, NewTailBB);
    OldMergeBB->getArgument(0)->replaceAllUsesWith(Apply);
  }

  if (!NeedsDefinition) {
    // Delete the outlined instructions/blocks.
    if (Release)
      Release->eraseFromParent();
    eraseBlock(OutlinedEntryBB);
    eraseBlock(switchInfo.NoneBB);
    eraseBlock(switchInfo.SomeBB);
    eraseBlock(OldMergeBB);
    return std::make_pair(nullptr, std::prev(StartBB->end()));
  }

  if (!OutlinedEntryBB->getParent()->hasOwnership())
    Fun->setOwnershipEliminated();

  Fun->setInlineStrategy(NoInline);

  // Move the blocks into the new function.
  Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin());
  Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, Fun->begin());
  Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, Fun->begin());
  Fun->moveBlockFromOtherFunction(OutlinedEntryBB, Fun->begin());

  // Create the function argument and return.
  auto *Load = dyn_cast<LoadInst>(FirstInst);
  SILBuilder Builder(FirstInst);
  if (Load) {
    OutlinedEntryBB->createFunctionArgument(Load->getOperand()->getType());
    auto *NewLoad =
        Builder.createLoad(Load->getLoc(), OutlinedEntryBB->getArgument(0),
                           Load->getOwnershipQualifier());
    Load->replaceAllUsesWith(NewLoad);
    Load->eraseFromParent();
  } else {
    OutlinedEntryBB->createFunctionArgument(
        FirstInst->getOperand(0)->getType());
    auto *Arg = OutlinedEntryBB->getArgument(0);
    FirstInst->setOperand(0, Arg);
    if (UnpairedRelease)
      UnpairedRelease->setOperand(0, Arg);
    PropApply->setArgument(0, Arg);
  }
  Builder.setInsertionPoint(OldMergeBB);
  Builder.createReturn(ObjCMethod->getLoc(), OldMergeBB->getArgument(0));
  return std::make_pair(Fun, std::prev(StartBB->end()));
}

#define ADVANCE_ITERATOR_OR_RETURN_FALSE(It)                                   \
  do {                                                                         \
    if (It->getParent()->end() == ++It)                                        \
      return false;                                                            \
  } while (0);

static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst,
                        SILValue SwitchOperand) {
  auto *SwitchEnum = dyn_cast<SwitchEnumInst>(Inst);
  if (!SwitchEnum || SwitchEnum->getNumCases() != 2 ||
      SwitchEnum->getOperand() != SwitchOperand)
    return false;

  auto *SwitchBB = SwitchEnum->getParent();
  SILBasicBlock *SomeBB = SwitchEnum->getCase(0).second;
  SILBasicBlock *NoneBB = SwitchEnum->getCase(1).second;
  if (NoneBB->getSinglePredecessorBlock() != SwitchBB)
    return false;
  if (SomeBB->getSinglePredecessorBlock() != SwitchBB)
    return false;
  if (NoneBB->args_size() == 1)
    std::swap(NoneBB, SomeBB);
  if (SomeBB->args_size() != 1 || NoneBB->args_size() != 0)
    return false;

  // bb9:
  // %43 = enum $Optional<String>, #Optional.none!enumelt
  auto It = NoneBB->begin();
  auto *NoneEnum = dyn_cast<EnumInst>(It);
  if (!NoneEnum || NoneEnum->hasOperand() || !NoneEnum->hasOneUse())
    return false;

  // br bb10(%43 : $Optional<String>)
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *Br1 = dyn_cast<BranchInst>(It);
  if (!Br1 || Br1->getNumArgs() != 1 || Br1->getArg(0) != NoneEnum)
    return false;
  auto *MergeBB = Br1->getDestBB();

  // bb8(%36 : $NSString):
  It = SomeBB->begin();
  auto *SomeBBArg = SomeBB->getArgument(0);
  if (!SomeBBArg->hasOneUse())
    return false;

  // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  auto *FunRef = dyn_cast<FunctionRefInst>(It);
  if (!FunRef || !FunRef->hasOneUse())
    return false;

  // %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *SomeEnum = dyn_cast<EnumInst>(It);
  if (!SomeEnum || !SomeEnum->hasOperand() || SomeEnum->getOperand() != SomeBBArg)
    return false;
  size_t numSomeEnumUses = std::distance(SomeEnum->use_begin(), SomeEnum->use_end());
  if (numSomeEnumUses > 2)
    return false;

  // %39 = metatype $@thin String.Type
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *Metatype = dyn_cast<MetatypeInst>(It);
  if (!Metatype || !Metatype->hasOneUse())
    return false;

  // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *Apply = dyn_cast<ApplyInst>(It);
  if (!Apply || !Apply->hasOneUse() || Apply->getCallee() != FunRef ||
      Apply->getNumArguments() != 2 || Apply->getArgument(0) != SomeEnum ||
      Apply->getArgument(1) != Metatype ||
      Apply->getSubstCalleeType()->getNumResults() != 1)
    return false;
  if (Apply->getSubstCalleeType()->getSingleResult().getConvention() !=
      ResultConvention::Owned)
    return false;

  // Check that we call the _unconditionallyBridgeFromObjectiveC witness.
  auto NativeType = Apply->getType().getASTType();
  auto *BridgeFun = FunRef->getReferencedFunction();
  // Not every type conforms to the ObjectiveCBridgeable protocol in such a case
  // getBridgeFromObjectiveC returns SILDeclRef().
  auto bridgeWitness = getBridgeFromObjectiveC(NativeType);
  if (bridgeWitness == SILDeclRef() ||
      BridgeFun->getName() != bridgeWitness.mangle())
    return false;

  // %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *Enum3 = dyn_cast<EnumInst>(It);
  if (!Enum3 || !Enum3->hasOneUse() || !Enum3->hasOperand() ||
      Enum3->getOperand() != Apply)
    return false;

  if (numSomeEnumUses == 2) {
    // [release_value | destroy_value] %38 : $Optional<NSString>
    ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
    bool hasOwnership = It->getFunction()->hasOwnership();
    if (hasOwnership) {
      auto *DVI = dyn_cast<DestroyValueInst>(It);
      if (!DVI || DVI->getOperand() != SomeEnum)
        return false;
    } else {
      auto *RVI = dyn_cast<ReleaseValueInst>(It);
      if (!RVI || RVI->getOperand() != SomeEnum)
        return false;
    }
  }

  // br bb10(%41 : $Optional<String>)
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  auto *Br = dyn_cast<BranchInst>(It);
  if (!Br || Br->getDestBB() != MergeBB || Br->getNumArgs() != 1 ||
      Br->getArg(0) != Enum3)
    return false;

  SI.SwitchEnum = SwitchEnum;
  SI.SomeBB = SomeBB;
  SI.NoneBB = NoneBB;
  SI.Br = Br;
  return true;
}

bool BridgedProperty::matchMethodCall(SILBasicBlock::iterator It,
                                      LoadInst *Load) {
  // Matches:
  //    %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
  //    %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
  //    switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
  //
  //  bb8(%36 : $NSString):
  //    %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  //    %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
  //    %39 = metatype $@thin String.Type
  //    %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  //    %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
  //    br bb10(%41 : $Optional<String>)
  //
  //  bb9:
  //    %43 = enum $Optional<String>, #Optional.none!enumelt
  //    br bb10(%43 : $Optional<String>)
  //
  //  bb10(%45 : $Optional<String>):
  //

  // %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign
  ObjCMethod = dyn_cast<ObjCMethodInst>(It);
  SILValue Instance =
      FirstInst != ObjCMethod ? FirstInst : ObjCMethod->getOperand();
  if (!ObjCMethod || !ObjCMethod->hasOneUse() ||
      ObjCMethod->getOperand() != Instance ||
      ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
      ObjCMethod->getType().castTo<SILFunctionType>()->isPolymorphic() ||
      ObjCMethod->getType().castTo<SILFunctionType>()->hasOpenedExistential())
    return false;

  // %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  PropApply = dyn_cast<ApplyInst>(It);
  if (!PropApply || PropApply->getCallee() != ObjCMethod ||
      PropApply->getNumArguments() != 1 ||
      PropApply->getArgument(0) != Instance || !PropApply->hasOneUse())
    return false;

  if (Load) {
    // In OSSA, there will be a destroy_value matching the earlier load [copy].
    // In non-ossa, there will be a release matching the earlier retain. The
    // only user of the retained value is the unowned objective-c method
    // consumer.
    unsigned NumUses = 0;
    Release = nullptr;
    bool hasOwnership = Load->getFunction()->hasOwnership();
    for (auto *Use : Load->getUses()) {
      ++NumUses;
      SILInstruction *R;
      if (hasOwnership) {
        R = dyn_cast<DestroyValueInst>(Use->getUser());
      } else {
        R = dyn_cast<StrongReleaseInst>(Use->getUser());
      }
      if (R) {
        if (!Release) {
          Release = R;
        } else {
          Release = nullptr;
          break;
        }
      }
    }
    if (!Release)
      return false;
    if (hasOwnership) {
      if (NumUses != 3)
        return false;
    } else {
      if (NumUses != 4)
        return false;
    }
    ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
    if (Release != &*It)
      return false;
  }

  ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  if (auto *dvi = dyn_cast<DestroyValueInst>(*&It)) {
    if (Load)
      return false;
    if (dvi->getOperand() != Instance)
      return false;
    UnpairedRelease = dvi;
    ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
  }

  // Don't outline in the outlined function.
  if (ObjCMethod->getFunction()->getName() == getOutlinedFunctionName())
    return false;

  // switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8,
  // case #Optional.none!enumelt: bb9
  return matchSwitch(switchInfo, &*It, PropApply);
}

bool BridgedProperty::matchInstSequence(SILBasicBlock::iterator It) {
  // Matches:
  // [ optionally:
  //    %31 = load %30 : $*UITextField or %31 = load [copy] %30 : $*UITextField
  //    strong_retain %31 : $UITextField
  // ]
  //    %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
  //    %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
  //    switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
  //
  //  bb8(%36 : $NSString):
  //    %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  //    %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
  //    %39 = metatype $@thin String.Type
  //    %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
  //    %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
  //    br bb10(%41 : $Optional<String>)
  //
  //  bb9:
  //    %43 = enum $Optional<String>, #Optional.none!enumelt
  //    br bb10(%43 : $Optional<String>)
  //
  //  bb10(%45 : $Optional<String>):

  clearState();

  // %31 = load %30 : $*UITextField
  auto *Load = dyn_cast<LoadInst>(It);

  // Otherwise, trying matching from the method call.
  if (!Load) {
    // Try to match without the load/strong_retain prefix.
    auto *CMI = dyn_cast<ObjCMethodInst>(It);
    if (!CMI || CMI->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
        CMI->getType().castTo<SILFunctionType>()->isPolymorphic() ||
        CMI->getType().castTo<SILFunctionType>()->hasOpenedExistential())
      return false;
    FirstInst = CMI;
  } else
    FirstInst = Load;

  StartBB = FirstInst->getParent();

  if (Load) {
    if (Load->getFunction()->hasOwnership()) {
      if (Load->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
        return false;
      ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
    } else {
      // strong_retain %31 : $UITextField
      ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
      auto *Retain = dyn_cast<StrongRetainInst>(It);
      if (!Retain || Retain->getOperand() != Load)
        return false;
      ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
    }
  }

  if (!matchMethodCall(It, Load))
    return false;

  return true;
}


namespace {

/// Match a bridged argument.
///
///
/// %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF
/// %16 = apply %15(%14) :
///         $@convention(method) (@guaranteed String) -> @owned NSString
/// %17 = enum $Optional<NSString>, #Optional.some!enumelt, %16 : $NSString
/// release_value %14 : $String
///
/// apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional<NSString> ...) ->
/// release_value %17 : $Optional<NSString>
///
/// NOTE: If release_value %14 is found, our outlined function will have an
/// owned convention for self. Otherwise, if we do not find it, we will have a
/// guaranteed one.
class BridgedArgument {
public:
  FunctionRefInst *BridgeFun;
  ApplyInst *BridgeCall;
  EnumInst *OptionalResult;
  SILValue BridgedValue;
  SILInstruction *ReleaseAfterBridge;
  SILInstruction *ReleaseArgAfterCall;
  unsigned Idx = 0;

  // Matched bridged argument.
  BridgedArgument(unsigned Idx, FunctionRefInst *F, ApplyInst *A, EnumInst *E,
                  SILInstruction *R0, SILInstruction *R1)
      : BridgeFun(F), BridgeCall(A), OptionalResult(E),
        BridgedValue(FullApplySite(A).getSelfArgument()),
        ReleaseAfterBridge(R0), ReleaseArgAfterCall(R1), Idx(Idx) {
    assert(!R0 || isa<ReleaseValueInst>(R0) || isa<DestroyValueInst>(R0));
  }

  /// Invalid argument constructor.
  BridgedArgument()
      : BridgeFun(nullptr), BridgeCall(nullptr), OptionalResult(nullptr),
        ReleaseAfterBridge(nullptr), ReleaseArgAfterCall(nullptr), Idx(0) {}

  static BridgedArgument match(unsigned ArgIdx, SILValue Arg, ApplyInst *AI,
                               DeadEndBlocks *deBlocks);

  operator bool() const { return BridgeFun != nullptr; }
  SILValue bridgedValue() { return BridgedValue; }

  bool isGuaranteed() const { return ReleaseAfterBridge == nullptr; }
  ParameterConvention getConvention() const {
    if (isGuaranteed())
      return ParameterConvention::Direct_Guaranteed;
    return ParameterConvention::Direct_Owned;
  }

  void eraseFromParent();

  /// Move the bridged argument sequence to the bridged call block.
  /// Precondition: The bridged call has already been moved to the outlined
  /// function.
  void transferTo(SILValue BridgedValueFunArg, ApplyInst *BridgedCall);
};
}

void BridgedArgument::transferTo(SILValue BridgedValue,
                                 ApplyInst *BridgedCall) {
  assert(BridgedCall->getParent() != BridgeFun->getParent());
  // Move the instructions to the bridged call that we have already moved and
  // update the uses of the bridge value by the function argument value passed
  // to this function.
  auto *DestBB = BridgedCall->getParent();
  DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeFun);
  DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeCall);
  BridgeCall->setArgument(0, BridgedValue);
  DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), OptionalResult);
  if (ReleaseAfterBridge) {
    DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), ReleaseAfterBridge);
    ReleaseAfterBridge->setOperand(0, BridgedValue);
  }
  auto AfterCall = std::next(SILBasicBlock::iterator(BridgedCall));
  DestBB->moveTo(SILBasicBlock::iterator(AfterCall), ReleaseArgAfterCall);
}

void BridgedArgument::eraseFromParent() {
  if (ReleaseAfterBridge)
    ReleaseAfterBridge->eraseFromParent();
  ReleaseArgAfterCall->eraseFromParent();
  OptionalResult->eraseFromParent();
  BridgeCall->eraseFromParent();
  BridgeFun->eraseFromParent();
}

static SILInstruction *findReleaseOf(SILValue releasedValue,
                                     SILBasicBlock::iterator from,
                                     SILBasicBlock::iterator to) {
  bool hasOwnership = releasedValue->getFunction()->hasOwnership();
  while (from != to) {
    if (hasOwnership) {
      auto destroy = dyn_cast<DestroyValueInst>(&*from);
      if (destroy && destroy->getOperand() == releasedValue)
        return destroy;
    } else {
      auto release = dyn_cast<ReleaseValueInst>(&*from);
      if (release && release->getOperand() == releasedValue)
        return release;
    }
    ++from;
  }
  return nullptr;
}

BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg,
                                       ApplyInst *AI, DeadEndBlocks *deBlocks) {
  // Match
  // %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF
  // %16 = apply %15(%14) : $@convention(method) (@guaranteed String) -> @owned NSString
  // %17 = enum $Optional<NSString>, #Optional.some!enumelt, %16 : $NSString
  // [release_value | destroy_value] %14 : $String
  // ...
  // apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional<NSString>...) ->
  // release_value ...
  // [release_value | destroy_value] %17 : $Optional<NSString>
  //
  auto *Enum = dyn_cast<EnumInst>(Arg);
  if (!Enum || !Enum->hasOperand())
    return BridgedArgument();

  if (SILBasicBlock::iterator(Enum) == Enum->getParent()->begin())
    return BridgedArgument();
  auto *BridgeCall =
      dyn_cast<ApplyInst>(std::prev(SILBasicBlock::iterator(Enum)));
  if (!BridgeCall || BridgeCall->getNumArguments() != 1 ||
      Enum->getOperand() != BridgeCall || !BridgeCall->hasOneUse() ||
      !FullApplySite(BridgeCall).hasSelfArgument())
    return BridgedArgument();

  auto &selfArg = FullApplySite(BridgeCall).getSelfArgumentOperand();
  auto selfConvention =
      FullApplySite(BridgeCall).getArgumentConvention(selfArg);
  if (selfConvention != SILArgumentConvention::Direct_Guaranteed &&
      selfConvention != SILArgumentConvention::Direct_Owned)
    return BridgedArgument();

  auto BridgedValue = BridgeCall->getArgument(0);
  auto Next = std::next(SILBasicBlock::iterator(Enum));
  if (Next == Enum->getParent()->end())
    return BridgedArgument();

  // Make sure that if we have a bridged value release that it is on the bridged
  // value.
  if (Enum->getParent() != AI->getParent())
    return BridgedArgument();

  bool hasOwnership = AI->getFunction()->hasOwnership();
  auto *BridgedValueRelease =
      findReleaseOf(BridgedValue, std::next(SILBasicBlock::iterator(Enum)),
                    SILBasicBlock::iterator(AI));
  assert(!BridgedValueRelease ||
         (hasOwnership && isa<DestroyValueInst>(BridgedValueRelease)) ||
         isa<ReleaseValueInst>(BridgedValueRelease));
  if (BridgedValueRelease && BridgedValueRelease->getOperand(0) != BridgedValue)
    return BridgedArgument();

  if (SILBasicBlock::iterator(BridgeCall) == BridgeCall->getParent()->begin())
    return BridgedArgument();

  auto *FunRef =
      dyn_cast<FunctionRefInst>(std::prev(SILBasicBlock::iterator(BridgeCall)));
  if (!FunRef || !FunRef->hasOneUse() || BridgeCall->getCallee() != FunRef)
    return BridgedArgument();

  SILInstruction *ReleaseAfter = nullptr;
  for (auto *Use : Enum->getUses()) {
    if (Use->getUser() == AI)
      continue;

    // The enum must only have two uses the release and the apply.
    if (ReleaseAfter)
      return BridgedArgument();

    if (hasOwnership) {
      ReleaseAfter = dyn_cast<DestroyValueInst>(Use->getUser());
    } else {
      ReleaseAfter = dyn_cast<ReleaseValueInst>(Use->getUser());
    }
    if (!ReleaseAfter)
      return BridgedArgument();
  }

  // Make sure we are calling the actual bridge witness.
  auto NativeType = BridgedValue->getType().getASTType();
  auto *BridgeFun = FunRef->getReferencedFunction();
  // Not every type conforms to the ObjectiveCBridgeable protocol in such a case
  // getBridgeToObjectiveC returns SILDeclRef().
  auto bridgeWitness = getBridgeToObjectiveC(NativeType);
  if (bridgeWitness == SILDeclRef() ||
      BridgeFun->getName() != bridgeWitness.mangle())
    return BridgedArgument();

  if (hasOwnership && !BridgedValueRelease) {
    SmallVector<Operand *> newUses{&AI->getOperandRef(ArgIdx)};
    if (!areUsesWithinValueLifetime(BridgedValue, newUses, deBlocks)) {
      return BridgedArgument();
    }
  }
  return BridgedArgument(ArgIdx, FunRef, BridgeCall, Enum, BridgedValueRelease,
                         ReleaseAfter);
}

namespace {
// Match the return value bridging pattern.
//   switch_enum %20 : $Optional<NSString>, case #O.some: bb1, case #O.none: bb2
//
// bb1(%23 : $NSString):
//   %24 = function_ref @_unconditionallyBridgeFromObjectiveC
//   %25 = enum $Optional<NSString>, #Optional.some!enumelt, %23 : $NSString
//   %26 = metatype $@thin String.Type
//   %27 = apply %24(%25, %26)
//   %28 = enum $Optional<String>, #Optional.some!enumelt, %27 : $String
//   br bb3(%28 : $Optional<String>)
//
// bb2:
//   %30 = enum $Optional<String>, #Optional.none!enumelt
//   br bb3(%30 : $Optional<String>)
//
// bb3(%32 : $Optional<String>):
class BridgedReturn {
  DeadEndBlocks *deBlocks;
  SwitchInfo switchInfo;
public:
  BridgedReturn(DeadEndBlocks *deBlocks) : deBlocks(deBlocks) {}

  bool match(ApplyInst *BridgedCall) {
    switchInfo = SwitchInfo();
    auto *SwitchBB = BridgedCall->getParent();
    return matchSwitch(switchInfo, SwitchBB->getTerminator(), BridgedCall);
  }

  operator bool() { return switchInfo.SomeBB != nullptr; }

  CanType getReturnType() {
    return switchInfo.Br->getArg(0)->getType().getASTType();
  }

  /// Outline the return value bridging blocks.
  void outline(SILFunction *Fun, ApplyInst *NewOutlinedCall);
};
}

void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) {
// Outline the bridged return result blocks.
//   switch_enum %20 : $Optional<NSString>, case #O.some: bb1, case #O.none: bb2
//
// bb1(%23 : $NSString):
//   %24 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveC
//   %25 = enum $Optional<NSString>, #Optional.some!enumelt, %23 : $NSString
//   %26 = metatype $@thin String.Type
//   %27 = apply %24(%25, %26)
//   %28 = enum $Optional<String>, #Optional.some!enumelt, %27 : $String
//   br bb3(%28 : $Optional<String>)
//
// bb2:
//   %30 = enum $Optional<String>, #Optional.none!enumelt
//   br bb3(%30 : $Optional<String>)
//
// bb3(%32 : $Optional<String>):

  auto *StartBB = switchInfo.SwitchEnum->getParent();
  auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(switchInfo.SwitchEnum));
  auto *OldMergeBB = switchInfo.Br->getDestBB();
  auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin());
  if (deBlocks) {
    deBlocks->updateForNewBlock(NewTailBB);
  }
  auto Loc = switchInfo.SwitchEnum->getLoc();

  {
    SILBuilder Builder(StartBB);
    Builder.createBranch(Loc, NewTailBB);
    OldMergeBB->getArgument(0)->replaceAllUsesWith(NewOutlinedCall);
  }

  // Outlined function already existed. Just delete instructions and wire up
  // blocks.
  if (!Fun) {
    eraseBlock(OutlinedEntryBB);
    eraseBlock(switchInfo.NoneBB);
    eraseBlock(switchInfo.SomeBB);
    eraseBlock(OldMergeBB);
    return;
  }

  // Move the blocks into the new function.
  assert(Fun->begin() != Fun->end() &&
         "The entry block must already have been created");
  SILBasicBlock *EntryBB = &*Fun->begin();
  Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin());
  Fun->moveBlockAfter(OldMergeBB, EntryBB);
	auto InsertPt = SILFunction::iterator(OldMergeBB);
  Fun->moveBlockFromOtherFunction(OutlinedEntryBB, InsertPt);
  Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, InsertPt);
  Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, InsertPt);

	SILBuilder Builder (EntryBB);
  Builder.createBranch(Loc, OutlinedEntryBB);

  Builder.setInsertionPoint(OldMergeBB);
  Builder.createReturn(Loc, OldMergeBB->getArgument(0));
}

namespace {
class ObjCMethodCall : public OutlinePattern {
  ObjCMethodInst *ObjCMethod;
  ApplyInst *BridgedCall;
  SmallVector<BridgedArgument, 4> BridgedArguments;
  std::string OutlinedName;
  llvm::BitVector IsBridgedArgument;
  llvm::BitVector IsGuaranteedArgument;
  ::BridgedReturn BridgedReturn;
public:
  bool matchInstSequence(SILBasicBlock::iterator I) override;

  std::pair<SILFunction *, SILBasicBlock::iterator>
  outline(SILModule &M) override;

  ObjCMethodCall(SILOptFunctionBuilder &FuncBuilder,
                 InstModCallbacks callbacks,
                 DeadEndBlocks *deBlocks)
      : OutlinePattern(FuncBuilder, callbacks, deBlocks), BridgedReturn(deBlocks) {}
  ~ObjCMethodCall();

private:
  void clearState();
  std::string getOutlinedFunctionName() override;
  CanSILFunctionType getOutlinedFunctionType(SILModule &M);
};
}

ObjCMethodCall::~ObjCMethodCall() {
  clearState();
}

void ObjCMethodCall::clearState() {
  ObjCMethod = nullptr;
  BridgedCall = nullptr;
  BridgedArguments.clear();
  OutlinedName.clear();
  IsBridgedArgument.clear();
  IsGuaranteedArgument.clear();
}

std::pair<SILFunction *, SILBasicBlock::iterator>
ObjCMethodCall::outline(SILModule &M) {

  auto FunctionType = getOutlinedFunctionType(M);
  std::string nameTmp = getOutlinedFunctionName();
  auto name = M.allocateCopy(nameTmp);

  auto *Fun = FuncBuilder.getOrCreateFunction(
      ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare,
      IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed,
      IsNotRuntimeAccessible);
  bool NeedsDefinition = Fun->empty();

  // Call the outlined function.
  ApplyInst *OutlinedCall;
  {
    SILBuilder Builder(BridgedCall);

    auto Loc = BridgedCall->getLoc();
    SILValue FunRef(Builder.createFunctionRef(Loc, Fun));

    // Collect the arguments for the apply.
    SmallVector<SILValue, 8> Args;
    unsigned OrigSigIdx = 0;
    unsigned BridgedArgIdx = 0;
    for (auto Arg : BridgedCall->getArguments()) {
      if (BridgedArgIdx < BridgedArguments.size() &&
          BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
        auto bridgedArgValue = BridgedArguments[BridgedArgIdx].bridgedValue();
        Args.push_back(bridgedArgValue);
        ++BridgedArgIdx;
      } else {
        // Otherwise, use the original type convention.
        Args.push_back(Arg);
      }
      ++OrigSigIdx;
    }
    OutlinedCall = Builder.createApply(Loc, FunRef, SubstitutionMap(), Args);
    if (!BridgedCall->use_empty() && !BridgedReturn)
      BridgedCall->replaceAllUsesWith(OutlinedCall);
  }

  // Outlined function already exists. Only need to delete basic blocks/instructions.
  if (!NeedsDefinition) {
		if (BridgedReturn)
      BridgedReturn.outline(nullptr, OutlinedCall);
    BridgedCall->eraseFromParent();
    ObjCMethod->eraseFromParent();
    // Remove bridged argument code.
    for (auto Arg : BridgedArguments)
      Arg.eraseFromParent();
    SILBasicBlock::iterator I(OutlinedCall);
    return std::make_pair(Fun, I);
  }

  if (!ObjCMethod->getFunction()->hasOwnership())
    Fun->setOwnershipEliminated();

  Fun->setInlineStrategy(NoInline);

  // Create the entry block.
  auto *EntryBB = Fun->createBasicBlock();

  // Move the bridged call.
  EntryBB->moveTo(EntryBB->end(), ObjCMethod);
  EntryBB->moveTo(EntryBB->end(), BridgedCall);

  // Create the arguments.
  unsigned OrigSigIdx = 0;
  unsigned BridgedArgIdx = 0;
  SILValue LastArg;
  for (auto Arg : BridgedCall->getArguments()) {
    if (BridgedArgIdx < BridgedArguments.size() &&
        BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
      auto &BridgedArg = BridgedArguments[BridgedArgIdx];
      auto *FunArg =
          EntryBB->createFunctionArgument(BridgedArg.bridgedValue()->getType());
      BridgedArg.transferTo(FunArg, BridgedCall);
      ++BridgedArgIdx;
    } else {
      auto *FunArg = EntryBB->createFunctionArgument(Arg->getType());
      BridgedCall->setArgument(OrigSigIdx, FunArg);
      LastArg = FunArg;
    }
    ++OrigSigIdx;
  }

  // Set the method lookup's target.
  ObjCMethod->setOperand(LastArg);

  // Create the return and optionally move the bridging code.
  if (!BridgedReturn) {
    SILBuilder Builder(EntryBB);
    Builder.createReturn(BridgedCall->getLoc(), BridgedCall);
  } else {
    BridgedReturn.outline(Fun, OutlinedCall);
  }

  SILBasicBlock::iterator I(OutlinedCall);
  return std::make_pair(Fun, I);
}

std::string ObjCMethodCall::getOutlinedFunctionName() {
  if (OutlinedName.empty()) {
    OutlinerMangler Mangler(ObjCMethod->getMember(), &IsBridgedArgument,
                            &IsGuaranteedArgument, BridgedReturn);
    OutlinedName = Mangler.mangle();
  }
  return OutlinedName;
}

bool ObjCMethodCall::matchInstSequence(SILBasicBlock::iterator I) {
  clearState();

  ObjCMethod = dyn_cast<ObjCMethodInst>(I);
  if (!ObjCMethod ||
      ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
      ObjCMethod->getType().castTo<SILFunctionType>()->isPolymorphic() ||
      ObjCMethod->getType().castTo<SILFunctionType>()->hasOpenedExistential())
    return false;

  auto *Use = ObjCMethod->getSingleUse();
  if (!Use)
    return false;
  BridgedCall = dyn_cast<ApplyInst>(Use->getUser());
  if (!BridgedCall ||
      (!BridgedCall->hasOneUse() && !BridgedCall->use_empty()) ||
      ObjCMethod->getParent() != BridgedCall->getParent())
    return false;

  // Collect bridged parameters.
  unsigned Idx = 0;
  IsBridgedArgument.resize(BridgedCall->getNumArguments(), false);
  IsGuaranteedArgument.resize(BridgedCall->getNumArguments(), false);
  // Map from owned values whose bridged versions we've seen in the apply to
  // the index in the apply at which they appear or UINT_MAX if we've seen them
  // more than once (which means we've already nulled out all the
  // BridgedArguments' ReleaseAfterBridge).
  llvm::DenseMap<SILValue, unsigned> seenOwnedBridgedValues;
  for (auto &Param : BridgedCall->getArgumentOperands()) {
    unsigned CurIdx = Idx++;

    // Look for Optional<NSFoo> type.
    auto Ty = Param.get()->getType().getOptionalObjectType();
    if (!Ty)
      continue;

    // Can't handle AnyObject. The concrete class type can be different since we
    // are passing 'id'. To make this work we would have to mangle the type into
    // the function name.
    if (Ty.isAnyObject())
      continue;

    auto BridgedArg =
        BridgedArgument::match(CurIdx, Param.get(), BridgedCall, deBlocks);
    if (!BridgedArg)
      continue;

    BridgedArguments.push_back(BridgedArg);
    IsBridgedArgument.set(CurIdx);
    if (BridgedArg.isGuaranteed()) {
      IsGuaranteedArgument.set(CurIdx);
      continue;
    }
    // Record that this owned value was used at CurIdx.
    auto pair =
        seenOwnedBridgedValues.insert({BridgedArg.BridgedValue, CurIdx});
    auto firstSighting = pair.second;
    if (firstSighting) {
      continue;
    }
    // This owned value was already seen.  Convert the current argument to
    // guaranteed and the previous argument as well if necessary.
    auto iterator = pair.first;
    if (iterator->second != UINT_MAX) {
      // This is the _second_ time the value has been seen.  Clear the previous
      // occurrence's ReleaseAfterBridge and sink the destroy after the apply.
      BridgedArguments[iterator->second].ReleaseAfterBridge->moveAfter(
          BridgedCall);
      BridgedArguments[iterator->second].ReleaseAfterBridge = nullptr;
      IsGuaranteedArgument.set(iterator->second);
      iterator->second = UINT_MAX;
    }
    BridgedArguments[CurIdx].ReleaseAfterBridge = nullptr;
    IsGuaranteedArgument.set(CurIdx);
  }

  // Try to match a bridged return value.
  BridgedReturn.match(BridgedCall);

  // Don't outline inside the outlined function.
  auto OutlinedName = getOutlinedFunctionName();
  auto CurrentName = ObjCMethod->getFunction()->getName();
  if (CurrentName == OutlinedName)
    return false;

  // Don't outline if we created an outlined function without the bridged result
  // from the outlined function with the bridged result (only the suffix is
  // different: MethodNameTem...n_ vs MethodNameTem...b_).
  if (OutlinedName.size() == CurrentName.size() &&
      CurrentName.starts_with(
          StringRef(OutlinedName.c_str(), OutlinedName.size() - 2)))
    return false;

  return !BridgedArguments.empty();
}

CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) {
  auto FunTy = BridgedCall->getSubstCalleeType();
  SmallVector<SILParameterInfo, 4> Parameters;
  unsigned OrigSigIdx = 0;
  unsigned BridgedArgIdx = 0;
  for (auto &ParamInfo : FunTy->getParameters()) {
    // Either use the bridged type passing it @owned.
    if (BridgedArgIdx < BridgedArguments.size() &&
        BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
      auto convention = BridgedArguments[BridgedArgIdx].getConvention();
      Parameters.push_back(SILParameterInfo(BridgedArguments[BridgedArgIdx]
                                                .bridgedValue()
                                                ->getType()
                                                .getASTType(),
                                            convention));
      ++BridgedArgIdx;
    } else {
      // Otherwise, use the original type convention.
      Parameters.push_back(ParamInfo);
    }
    ++OrigSigIdx;
  }

  auto ExtInfo = SILFunctionType::ExtInfo::getThin();

  SmallVector<SILResultInfo, 4> Results;
  // If we don't have a bridged return we changed from @autoreleased to @owned
  // if there is a result.
  if (!BridgedReturn) {
    if (FunTy->getNumResults()) {
      auto OrigResultInfo = FunTy->getSingleResult();
      Results.push_back(SILResultInfo(OrigResultInfo.getInterfaceType(),
                                      OrigResultInfo.getConvention() ==
                                              ResultConvention::Autoreleased
                                          ? ResultConvention::Owned
                                          : OrigResultInfo.getConvention()));
    }
  } else {
    // Otherwise, we used the bridged return type.
    Results.push_back(
        SILResultInfo(BridgedReturn.getReturnType(), ResultConvention::Owned));
  }
  auto FunctionType = SILFunctionType::get(
      nullptr, ExtInfo, SILCoroutineKind::None,
      ParameterConvention::Direct_Unowned, Parameters, {}, Results,
      std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext());
  return FunctionType;
}

namespace {
/// A collection of outlineable patterns.
class OutlinePatterns {
  BridgedProperty BridgedPropertyPattern;
  ObjCMethodCall ObjCMethodCallPattern;

public:
  /// Try matching an outlineable pattern from the current instruction.
  OutlinePattern *tryToMatch(SILBasicBlock::iterator CurInst) {
    if (BridgedPropertyPattern.matchInstSequence(CurInst))
      return &BridgedPropertyPattern;

    if (ObjCMethodCallPattern.matchInstSequence(CurInst))
      return &ObjCMethodCallPattern;

    return nullptr;
  }

  OutlinePatterns(SILOptFunctionBuilder &FuncBuilder,
                  InstModCallbacks callbacks,
                  DeadEndBlocks *deBlocks)
      : BridgedPropertyPattern(FuncBuilder, callbacks, deBlocks),
        ObjCMethodCallPattern(FuncBuilder, callbacks, deBlocks) {}
  ~OutlinePatterns() {}

  OutlinePatterns(const OutlinePatterns&) = delete;
  OutlinePatterns& operator=(const OutlinePatterns) = delete;
};
} // end anonymous namespace.


/// Perform outlining on the function and return any newly created outlined
/// functions.
bool tryOutline(SILOptFunctionBuilder &FuncBuilder, SILFunction *Fun,
                SmallVectorImpl<SILFunction *> &FunctionsAdded,
                InstModCallbacks callbacks = InstModCallbacks(),
                DeadEndBlocks *deBlocks = nullptr) {
  BasicBlockWorklist Worklist(Fun->getEntryBlock());
  OutlinePatterns patterns(FuncBuilder, callbacks, deBlocks);
  bool changed = false;

  // Traverse the function.
  while (SILBasicBlock *CurBlock = Worklist.pop()) {
    SILBasicBlock::iterator CurInst = CurBlock->begin();

    // Go over the instructions trying to match and replace patterns.
    while (CurInst != CurBlock->end()) {
       if (OutlinePattern *match = patterns.tryToMatch(CurInst)) {
         SILFunction *F;
         SILBasicBlock::iterator LastInst;
         std::tie(F, LastInst) = match->outline(Fun->getModule());
         if (F)
           FunctionsAdded.push_back(F);
         CurInst = LastInst;
         assert(LastInst->getParent() == CurBlock);
         changed = true;
       } else if (isa<TermInst>(CurInst)) {
         for (SILBasicBlock *succ : CurBlock->getSuccessors()) {
           Worklist.pushIfNotVisited(succ);
         }
         ++CurInst;
       } else {
         ++CurInst;
       }
    }
  }
  return changed;
}

namespace {

class Outliner : public SILFunctionTransform {
public:
  Outliner() { }

  void run() override {
    auto *Fun = getFunction();

    // Only outline if we optimize for size.
    if (!Fun->optimizeForSize())
      return;

    // Dump function if requested.
    if (DumpFuncsBeforeOutliner.size() &&
        Fun->getName().contains(DumpFuncsBeforeOutliner)) {
      Fun->dump();
    }

    DeadEndBlocksAnalysis *deBlocksAnalysis =
        PM->getAnalysis<DeadEndBlocksAnalysis>();
    DeadEndBlocks *deBlocks = deBlocksAnalysis->get(Fun);
    InstModCallbacks callbacks;

    SILOptFunctionBuilder FuncBuilder(*this);
    SmallVector<SILFunction *, 16> FunctionsAdded;
    bool Changed = false;

    if (Fun->hasOwnership()) {
      Changed =
          tryOutline(FuncBuilder, Fun, FunctionsAdded, callbacks, deBlocks);
    } else {
      Changed = tryOutline(FuncBuilder, Fun, FunctionsAdded);
    }

    if (!FunctionsAdded.empty()) {
      // Notify the pass manager of any new functions we outlined.
      for (auto *AddedFunc : FunctionsAdded) {
        addFunctionToPassManagerWorklist(AddedFunc, nullptr);
      }
    }

    if (Changed) {
      invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
    }
  }
};

} //end anonymous namespace.

SILTransform *swift::createOutliner() {
  return new Outliner();
}
