//===--- CSE.cpp - Simple and fast CSE pass -------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This pass performs a simple dominator tree walk that eliminates trivially
// redundant instructions.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-cse"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/Dominance.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/NodeBits.h"
#include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/SILCloner.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
#include "swift/SILOptimizer/Utils/DebugOptUtils.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
#include "swift/SILOptimizer/Utils/SILInliner.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "swift/SILOptimizer/Utils/StackNesting.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/RecyclingAllocator.h"

STATISTIC(NumOpenExtRemoved,
          "Number of open_existential_addr instructions removed");

STATISTIC(NumSimplify, "Number of instructions simplified or DCE'd");
STATISTIC(NumCSE,      "Number of instructions CSE'd");

using namespace swift;

//===----------------------------------------------------------------------===//
//                                Simple Value
//===----------------------------------------------------------------------===//

namespace {
/// SimpleValue - Instances of this struct represent available values in the
/// scoped hash table.
struct SimpleValue {
  SILInstruction *Inst;

  SimpleValue(SILInstruction *I) : Inst(I) { }

  bool isSentinel() const {
    return Inst == llvm::DenseMapInfo<SILInstruction *>::getEmptyKey() ||
           Inst == llvm::DenseMapInfo<SILInstruction *>::getTombstoneKey();
  }
};
} // end anonymous namespace

namespace llvm {
template <> struct DenseMapInfo<SimpleValue> {
  static inline SimpleValue getEmptyKey() {
    return DenseMapInfo<SILInstruction *>::getEmptyKey();
  }
  static inline SimpleValue getTombstoneKey() {
    return DenseMapInfo<SILInstruction *>::getTombstoneKey();
  }
  static unsigned getHashValue(SimpleValue Val);
  static bool isEqual(SimpleValue LHS, SimpleValue RHS);
};
} // end namespace llvm

SILValue tryLookThroughOwnershipInsts(const Operand *op) {
  auto opValue = op->get();
  auto opOwnership = op->getOperandOwnership();

  // Escaped values are dependent on the base value lifetime.
  // OSSA RAUW does not lifetime extend base value for an escaped value.
  // Don't look through ownership instructions for such values.

  // Theoritically, it should be possible to look through ownership instructions
  // for a bitwise escape, barring any dependent instructions like
  // mark_dependence. Not doing it here to be conservative.
  if (opOwnership == OperandOwnership::PointerEscape ||
      opOwnership == OperandOwnership::BitwiseEscape ||
      opOwnership == OperandOwnership::ForwardingUnowned) {
    return opValue;
  }

  return lookThroughOwnershipInsts(opValue);
}

namespace {
class HashVisitor : public SILInstructionVisitor<HashVisitor, llvm::hash_code> {
  using hash_code = llvm::hash_code;

public:
  hash_code visitSILInstruction(SILInstruction *) {
    llvm_unreachable("No hash implemented for the given type");
  }

  hash_code visitBridgeObjectToRefInst(BridgeObjectToRefInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitBridgeObjectToWordInst(BridgeObjectToWordInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitClassifyBridgeObjectInst(ClassifyBridgeObjectInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitValueToBridgeObjectInst(ValueToBridgeObjectInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitRefToBridgeObjectInst(RefToBridgeObjectInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getType(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitUncheckedAddrCastInst(UncheckedAddrCastInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitFunctionRefInst(FunctionRefInst *X) {
    return llvm::hash_combine(X->getKind(), X->getReferencedFunction());
  }

  hash_code visitGlobalAddrInst(GlobalAddrInst *X) {
    return llvm::hash_combine(X->getKind(), X->getReferencedGlobal());
  }

  hash_code visitIntegerLiteralInst(IntegerLiteralInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType(), X->getValue());
  }

  hash_code visitFloatLiteralInst(FloatLiteralInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType(), X->getBits());
  }

  hash_code visitRefElementAddrInst(RefElementAddrInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getField());
  }

  hash_code visitRefTailAddrInst(RefTailAddrInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitProjectBoxInst(ProjectBoxInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitRefToRawPointerInst(RefToRawPointerInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitRawPointerToRefInst(RawPointerToRefInst *X) {
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

#define LOADABLE_REF_STORAGE(Name, ...)                                        \
  hash_code visit##Name##ToRefInst(Name##ToRefInst *X) {                       \
    return llvm::hash_combine(                                                 \
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));      \
  }                                                                            \
  hash_code visitRefTo##Name##Inst(RefTo##Name##Inst *X) {                     \
    return llvm::hash_combine(                                                 \
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()));      \
  }
#include "swift/AST/ReferenceStorage.def"

  hash_code visitUpcastInst(UpcastInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitStringLiteralInst(StringLiteralInst *X) {
    return llvm::hash_combine(X->getKind(), X->getEncoding(), X->getValue());
  }

  hash_code visitStructInst(StructInst *X) {
    // This is safe since we are hashing the operands using the actual pointer
    // values of the values being used by the operand.
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getStructDecl(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getStructDecl(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitStructExtractInst(StructExtractInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getStructDecl(), X->getField(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitStructElementAddrInst(StructElementAddrInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getStructDecl(), X->getField(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitCondFailInst(CondFailInst *X) {
    return llvm::hash_combine(X->getKind(), X->getOperand());
  }

  hash_code visitClassMethodInst(ClassMethodInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitSuperMethodInst(SuperMethodInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitTupleInst(TupleInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getTupleType(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getTupleType(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitTupleExtractInst(TupleExtractInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getTupleType(), X->getFieldIndex(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitTupleElementAddrInst(TupleElementAddrInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getTupleType(), X->getFieldIndex(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitMetatypeInst(MetatypeInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType());
  }

  hash_code visitValueMetatypeInst(ValueMetatypeInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitExistentialMetatypeInst(ExistentialMetatypeInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType());
  }

  hash_code visitInitExistentialMetatypeInst(InitExistentialMetatypeInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()),
        llvm::hash_combine_range(X->getConformances().begin(),
                                 X->getConformances().end()));
  }

  hash_code visitObjCProtocolInst(ObjCProtocolInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType(), X->getProtocol());
  }

  hash_code visitIndexRawPointerInst(IndexRawPointerInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getBaseOperandRef()), X->getIndex());
  }

  hash_code visitPointerToAddressInst(PointerToAddressInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->isStrict());
  }

  hash_code visitAddressToPointerInst(AddressToPointerInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitApplyInst(ApplyInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getCallee(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getCallee(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitBuiltinInst(BuiltinInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getName().get(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()),
          X->hasSubstitutions());
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getName().get(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()),
        X->hasSubstitutions());
  }

  hash_code visitEnumInst(EnumInst *X) {
    // We hash the enum by hashing its kind, element, and operand if it has one.
    if (!X->hasOperand())
      return llvm::hash_combine(X->getKind(), X->getElement());
    return llvm::hash_combine(
        X->getKind(), X->getElement(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitUncheckedEnumDataInst(UncheckedEnumDataInst *X) {
    // We hash the enum by hashing its kind, element, and operand.
    return llvm::hash_combine(
        X->getKind(), X->getElement(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitIndexAddrInst(IndexAddrInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        tryLookThroughOwnershipInsts(&X->getBaseOperandRef()), X->getIndex());
  }

  hash_code visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getType());
  }

  hash_code visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getType());
  }

  hash_code visitObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getType());
  }

  hash_code visitObjCExistentialMetatypeToObjectInst(
      ObjCExistentialMetatypeToObjectInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getType());
  }

  hash_code visitUncheckedRefCastInst(UncheckedRefCastInst *X) {
    return llvm::hash_combine(X->getKind(),
                              tryLookThroughOwnershipInsts(&X->getOperandRef()),
                              X->getType());
  }

  hash_code visitSelectEnumOperation(SelectEnumOperation X) {
    auto hash = llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X.getEnumOperandRef()),
        X->getType(), X.hasDefault());

    for (unsigned i = 0, e = X.getNumCases(); i < e; ++i) {
      hash = llvm::hash_combine(hash, X.getCase(i).first, X.getCase(i).second);
    }

    if (X.hasDefault())
      hash = llvm::hash_combine(hash, X.getDefaultResult());

    return hash;
  }

  hash_code visitSelectEnumInst(SelectEnumInst *X) {
    return visitSelectEnumOperation(X);
  }

  hash_code visitSelectEnumAddrInst(SelectEnumAddrInst *X) {
    return visitSelectEnumOperation(X);
  }

  hash_code visitWitnessMethodInst(WitnessMethodInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getLookupType().getPointer(), X->getMember(),
          X->getConformance(), X->getType(),
          !X->getTypeDependentOperands().empty(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }

    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getLookupType().getPointer(), X->getMember(),
        X->getConformance(), X->getType(),
        !X->getTypeDependentOperands().empty(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitMarkDependenceInst(MarkDependenceInst *X) {
    if (X->getFunction()->hasOwnership()) {
      auto TransformedOpValues =
          X->getOperandValues(tryLookThroughOwnershipInsts, false);
      return llvm::hash_combine(
          X->getKind(), X->getType(),
          llvm::hash_combine_range(TransformedOpValues.begin(),
                                   TransformedOpValues.end()));
    }
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(), X->getType(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()));
  }

  hash_code visitOpenExistentialRefInst(OpenExistentialRefInst *X) {
    auto ArchetypeTy = X->getType().castTo<ArchetypeType>();
    auto ConformsTo = ArchetypeTy->getConformsTo();
    return llvm::hash_combine(
        X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()),
        llvm::hash_combine_range(ConformsTo.begin(), ConformsTo.end()));
  }

  hash_code visitScalarPackIndexInst(ScalarPackIndexInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getIndexedPackType(), X->getComponentIndex());
  }

  hash_code visitDynamicPackIndexInst(DynamicPackIndexInst *X) {
    return llvm::hash_combine(
        X->getKind(), X->getIndexedPackType(),
        tryLookThroughOwnershipInsts(&X->getOperandRef()));
  }

  hash_code visitTuplePackElementAddrInst(TuplePackElementAddrInst *X) {
    OperandValueArrayRef Operands(X->getAllOperands());
    return llvm::hash_combine(
        X->getKind(),
        llvm::hash_combine_range(Operands.begin(), Operands.end()),
        X->getElementType());
  }

  hash_code visitTypeValueInst(TypeValueInst *X) {
    return llvm::hash_combine(X->getKind(), X->getType(), X->getParamType());
  }
};
} // end anonymous namespace

unsigned llvm::DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) {
  return HashVisitor().visit(Val.Inst);
}

bool llvm::DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS,
                                              SimpleValue RHS) {
  SILInstruction *LHSI = LHS.Inst, *RHSI = RHS.Inst;
  if (LHS.isSentinel() || RHS.isSentinel())
    return LHSI == RHSI;

  auto LOpen = dyn_cast<OpenExistentialRefInst>(LHSI);
  auto ROpen = dyn_cast<OpenExistentialRefInst>(RHSI);
  if (LOpen && ROpen) {
    // Check operands.
    auto *LOp = &LOpen->getOperandRef();
    auto *ROp = &ROpen->getOperandRef();
    if (tryLookThroughOwnershipInsts(LOp) != tryLookThroughOwnershipInsts(ROp))
      return false;

    // Consider the types of two open_existential_ref instructions to be equal,
    // if the sets of protocols they conform to are equal ...
    auto LHSArchetypeTy = LOpen->getType().castTo<ArchetypeType>();
    auto RHSArchetypeTy = ROpen->getType().castTo<ArchetypeType>();

    auto LHSConformsTo = LHSArchetypeTy->getConformsTo();
    auto RHSConformsTo = RHSArchetypeTy->getConformsTo();
    if (LHSConformsTo != RHSConformsTo)
      return false;

    // ... and other constraints are equal.
    if (LHSArchetypeTy->getSuperclass().getPointer() !=
        RHSArchetypeTy->getSuperclass().getPointer())
      return false;

    if (LHSArchetypeTy->getLayoutConstraint() !=
        RHSArchetypeTy->getLayoutConstraint())
      return false;

    return true;
  }
  auto *ltvi = dyn_cast<TypeValueInst>(LHSI);
  auto *rtvi = dyn_cast<TypeValueInst>(RHSI);
  if (ltvi && rtvi) {
    if (ltvi->getType() != rtvi->getType())
      return false;
    return ltvi->getParamType() == rtvi->getParamType();
  }
  auto opCmp = [&](const Operand *op1, const Operand *op2) -> bool {
    if (op1 == op2)
      return true;
    if (tryLookThroughOwnershipInsts(op1) == tryLookThroughOwnershipInsts(op2))
      return true;
    return false;
  };
  bool isEqual =
      LHSI->getKind() == RHSI->getKind() && LHSI->isIdenticalTo(RHSI, opCmp);
#ifndef NDEBUG
  if (isEqual && getHashValue(LHS) != getHashValue(RHS)) {
    llvm::dbgs() << "LHS: ";
    LHSI->dump();
    llvm::dbgs() << "RHS: ";
    RHSI->dump();
    llvm::dbgs() << "In function:\n";
    LHSI->getFunction()->dump();
    llvm_unreachable("Mismatched isEqual and getHashValue() function in CSE\n");
  }
#endif
  return isEqual;
}

namespace {
// A very simple cloner for cloning instructions inside
// the same function. The only interesting thing it does
// is remapping the archetypes when it is required.
class InstructionCloner : public SILCloner<InstructionCloner> {
  friend class SILCloner<InstructionCloner>;
  friend class SILInstructionVisitor<InstructionCloner>;
  SILInstruction *Result = nullptr;

public:
  InstructionCloner(SILFunction *F) : SILCloner(*F) {}

  static SILInstruction *doIt(SILInstruction *I) {
    InstructionCloner TC(I->getFunction());
    return TC.clone(I);
  }

  SILInstruction *clone(SILInstruction *I) {
    visit(I);
    return Result;
  }

  void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
    assert(Orig->getFunction() == &getBuilder().getFunction() &&
           "cloning between functions is not supported");

    Result = Cloned;
    SILCloner<InstructionCloner>::postProcess(Orig, Cloned);
  }
  SILValue getMappedValue(SILValue Value) { return Value; }
  SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
//                               CSE Interface
//===----------------------------------------------------------------------===//

namespace swift {

/// CSE - This pass does a simple depth-first walk over the dominator tree,
/// eliminating trivially redundant instructions and using simplifyInstruction
/// to canonicalize things as it goes. It is intended to be fast and catch
/// obvious cases so that SILCombine and other passes are more effective.
///
/// It also optimizes calls to lazy property getters: If such a call is
/// dominated by another call to the same getter, it is replaced by a direct
/// load of the property - assuming that it is already computed.
class CSE {
public:
  typedef llvm::ScopedHashTableVal<SimpleValue, ValueBase *> SimpleValueHTType;
  typedef llvm::RecyclingAllocator<llvm::BumpPtrAllocator, SimpleValueHTType>
  AllocatorTy;
  typedef llvm::ScopedHashTable<SimpleValue, SILInstruction *,
                                llvm::DenseMapInfo<SimpleValue>,
                                AllocatorTy> ScopedHTType;

  /// AvailableValues - This scoped hash table contains the current values of
  /// all of our simple scalar expressions.  As we walk down the domtree, we
  /// look to see if instructions are in this: if so, we replace them with what
  /// we find, otherwise we insert them so that dominated values can succeed in
  /// their lookup.
  ScopedHTType *AvailableValues;

  BasicCalleeAnalysis *BCA;

  SILOptFunctionBuilder &FuncBuilder;

  DeadEndBlocks &DeadEndBBs;

  OwnershipFixupContext &RAUWFixupContext;

  /// The set of calls to lazy property getters which can be replace by a direct
  /// load of the property value.
  llvm::SmallVector<ApplyInst *, 8> lazyPropertyGetters;

  CSE(bool RunsOnHighLevelSil, BasicCalleeAnalysis *BCA,
      SILOptFunctionBuilder &FuncBuilder, DeadEndBlocks &DeadEndBBs,
      OwnershipFixupContext &RAUWFixupContext)
      : BCA(BCA), FuncBuilder(FuncBuilder), DeadEndBBs(DeadEndBBs),
        RAUWFixupContext(RAUWFixupContext),
        RunsOnHighLevelSil(RunsOnHighLevelSil) {}

  bool processFunction(SILFunction &F, DominanceInfo *DT);

  bool processLazyPropertyGetters(SILFunction &F);

  bool canHandle(SILInstruction *Inst);

private:
  
  /// True if CSE is done on high-level SIL, i.e. semantic calls are not inlined
  /// yet. In this case some semantic calls can be CSEd.
  bool RunsOnHighLevelSil;
  
  // NodeScope - almost a POD, but needs to call the constructors for the
  // scoped hash tables so that a new scope gets pushed on. These are RAII so
  // that the scope gets popped when the NodeScope is destroyed.
  class NodeScope {
   public:
    NodeScope(ScopedHTType *availableValues) : Scope(*availableValues) {}

   private:
    NodeScope(const NodeScope &) = delete;
    void operator=(const NodeScope &) = delete;

    ScopedHTType::ScopeTy Scope;
  };

  // StackNode - contains all the needed information to create a stack for doing
  // a depth first traversal of the tree. This includes scopes for values and
  // loads as well as the generation. There is a child iterator so that the
  // children do not need to be store separately.
  class StackNode {
   public:
    StackNode(ScopedHTType *availableValues, DominanceInfoNode *n,
              DominanceInfoNode::const_iterator child,
              DominanceInfoNode::const_iterator end)
        : Node(n), ChildIter(child), EndIter(end), Scopes(availableValues),
      Processed(false) {}

    // Accessors.
    DominanceInfoNode *node() { return Node; }
    DominanceInfoNode::const_iterator childIter() { return ChildIter; }
    DominanceInfoNode *nextChild() {
      DominanceInfoNode *child = *ChildIter;
      ++ChildIter;
      return child;
    }
    DominanceInfoNode::const_iterator end() { return EndIter; }
    bool isProcessed() { return Processed; }
    void process() { Processed = true; }

   private:
    StackNode(const StackNode &) = delete;
    void operator=(const StackNode &) = delete;

    // Members.
    DominanceInfoNode *Node;
    DominanceInfoNode::const_iterator ChildIter;
    DominanceInfoNode::const_iterator EndIter;
    NodeScope Scopes;
    bool Processed;
  };

  bool processNode(DominanceInfoNode *Node);
  bool processOpenExistentialRef(OpenExistentialRefInst *Inst,
                                 OpenExistentialRefInst *V);
};
} // namespace swift

//===----------------------------------------------------------------------===//
//                             CSE Implementation
//===----------------------------------------------------------------------===//

bool CSE::processFunction(SILFunction &Fm, DominanceInfo *DT) {
  std::vector<StackNode *> nodesToProcess;

  // Tables that the pass uses when walking the domtree.
  ScopedHTType AVTable;
  AvailableValues = &AVTable;

  bool Changed = false;

  // Process the root node.
  nodesToProcess.push_back(new StackNode(AvailableValues, DT->getRootNode(),
                  DT->getRootNode()->begin(),
                  DT->getRootNode()->end()));

  // Process the stack.
  while (!nodesToProcess.empty()) {
    // Grab the first item off the stack. Set the current generation, remove
    // the node from the stack, and process it.
    StackNode *NodeToProcess = nodesToProcess.back();

    // Check if the node needs to be processed.
    if (!NodeToProcess->isProcessed()) {
      // Process the node.
      Changed |= processNode(NodeToProcess->node());
      NodeToProcess->process();

    } else if (NodeToProcess->childIter() != NodeToProcess->end()) {
      // Push the next child onto the stack.
      DominanceInfoNode *child = NodeToProcess->nextChild();
      nodesToProcess.push_back(
          new StackNode(AvailableValues, child, child->begin(), child->end()));
    } else {
      // It has been processed, and there are no more children to process,
      // so delete it and pop it off the stack.
      delete NodeToProcess;
      nodesToProcess.pop_back();
    }
  } // while (!nodes...)

  return Changed;
}

/// Replace lazy property getters (which are dominated by the same getter)
/// by a direct load of the value.
bool CSE::processLazyPropertyGetters(SILFunction &F) {
  bool changed = false;
  bool invalidatedStackNesting = false;
  for (ApplyInst *ai : lazyPropertyGetters) {
    SILFunction *getter = ai->getReferencedFunctionOrNull();
    assert(getter && getter->isLazyPropertyGetter());
    SILBasicBlock *callBlock = ai->getParent();

    // Inline the getter...
    InstructionDeleter deleter;
    SILInliner::inlineFullApply(ai, SILInliner::InlineKind::PerformanceInline,
                                FuncBuilder, deleter);
    deleter.cleanupDeadInstructions();
    
    // ...and fold the switch_enum in the first block to the Optional.some case.
    // The Optional.none branch becomes dead.
    auto *sei = cast<SwitchEnumInst>(callBlock->getTerminator());
    ASTContext &ctxt = callBlock->getParent()->getModule().getASTContext();
    EnumElementDecl *someDecl = ctxt.getOptionalSomeDecl();
    SILBasicBlock *someDest = sei->getCaseDestination(someDecl);
    assert(someDest->getNumArguments() == 1);
    SILValue enumVal = sei->getOperand();
    SILBuilder builder(sei);
    SILType ty = enumVal->getType().getEnumElementType(someDecl,
                           sei->getModule(), builder.getTypeExpansionContext());
    auto *ued =
        builder.createUncheckedEnumData(sei->getLoc(), enumVal, someDecl, ty);
    builder.createBranch(sei->getLoc(), someDest, { ued });
    sei->eraseFromParent();
    // When inlining an OSSA function into a non-OSSA function, ownership of
    // nonescaping closures is lowered.  At that point, they are recognized as
    // stack users.  Since they weren't recognized as such before, they may not
    // satisfy stack discipline.  Fix that up now.
    if (getter->hasOwnership() && !ai->getFunction()->hasOwnership()) {
      invalidatedStackNesting = true;
    }
    changed = true;
    ++NumCSE;
  }
  if (invalidatedStackNesting) {
    StackNesting::fixNesting(&F);
  }
  return changed;
}

/// Update SIL basic block's arguments types which refer to opened
/// archetypes. Replace such types by performing type substitutions
/// according to the provided type substitution map.
static void updateBasicBlockArgTypes(SILBasicBlock *BB,
                                     InstructionCloner &Cloner,
                                     InstructionWorklist &usersToHandle) {
  // Check types of all BB arguments.
  for (auto *Arg : BB->getSILPhiArguments()) {
    if (!Arg->getType().hasOpenedExistential())
      continue;
    // Type of this BB argument uses an opened existential.
    // Try to apply substitutions to it and if it produces a different type,
    // use this type as new type of the BB argument.
    auto OldArgType = Arg->getType();

    auto NewArgType = Cloner.getOpType(OldArgType);
    if (NewArgType == Arg->getType())
      continue;
    // Replace the type of this BB argument. The type of a BBArg
    // can only be changed using replaceBBArg, if the BBArg has no uses.
    // So, make it look as if it has no uses.

    // First collect all uses, before changing the type.
    SmallVector<Operand *, 4> OriginalArgUses;
    for (auto *ArgUse : Arg->getUses()) {
      OriginalArgUses.push_back(ArgUse);
    }
    // Then replace all uses by an undef.
    Arg->replaceAllUsesWith(SILUndef::get(Arg));
    // Replace the type of the BB argument.
    auto *NewArg = BB->replacePhiArgument(Arg->getIndex(), NewArgType,
                                          Arg->getOwnershipKind(),
                                          Arg->getDecl());
    // Restore all uses to refer to the BB argument with updated type.
    for (auto ArgUse : OriginalArgUses) {
      ArgUse->set(NewArg);
      usersToHandle.pushIfNotVisited(ArgUse->getUser());
    }
  }
}

/// Handle CSE of open_existential_ref instructions.
/// Returns true if uses of open_existential_ref can
/// be replaced by a dominating instruction.
/// \Inst is the open_existential_ref instruction
/// \V is the dominating open_existential_ref instruction
bool CSE::processOpenExistentialRef(OpenExistentialRefInst *Inst,
                                    OpenExistentialRefInst *VI) {
  InstructionWorklist usersToHandle(Inst->getFunction());
  const auto OldOpenedArchetype = Inst->getDefinedOpenedArchetype();
  const auto NewOpenedArchetype = VI->getDefinedOpenedArchetype();

  // Collect all candidates that may contain opened archetypes
  // that need to be replaced.
  for (auto Use : Inst->getUses()) {
    auto User = Use->getUser();
    if (!User->getTypeDependentOperands().empty()) {
      if (canHandle(User)) {
        auto It = AvailableValues->begin(User);
        if (It != AvailableValues->end()) {
          return false;
        }
      }
    }
    usersToHandle.pushIfNotVisited(User);
  }

  auto *OldEnv = OldOpenedArchetype->getGenericEnvironment();
  auto *NewEnv = NewOpenedArchetype->getGenericEnvironment();

  // Now process candidates.
  // Use a cloner. It makes copying the instruction and remapping of
  // opened archetypes trivial.
  InstructionCloner Cloner(Inst->getFunction());
  Cloner.registerLocalArchetypeRemapping(OldEnv, NewEnv);
  auto &Builder = Cloner.getBuilder();

  // Now clone each candidate and replace the opened archetype
  // by a dominating one.
  while (SILInstruction *user = usersToHandle.pop()) {
    if (isa<TermInst>(user)) {
      // The current use of the opened archetype is a terminator instruction.
      // Check if any of the successor BBs uses this opened archetype in the
      // types of its basic block arguments. If this is the case, replace
      // those uses by the new opened archetype.
      for (auto *Successor : user->getParent()->getSuccessorBlocks()) {
        if (Successor->args_empty())
          continue;
        // If a BB has any arguments, update their types if necessary.
        updateBasicBlockArgTypes(Successor, Cloner, usersToHandle);
      }
    }

    // Compute if a candidate depends on the old opened archetype.
    // It always does if it has any type-dependent operands.
    bool DependsOnOldOpenedArchetype =
      !user->getTypeDependentOperands().empty();

    // Look for dependencies propagated via the candidate's results.
    for (auto result : user->getResults()) {
      if (result->use_empty() || !result->getType().hasOpenedExistential())
        continue;

      // Check if the result type depends on this specific opened existential.
      auto ResultDependsOnOldOpenedArchetype =
          result->getType().getASTType()->hasLocalArchetypeFromEnvironment(OldEnv);

      // If it does, the candidate depends on the opened existential.
      if (ResultDependsOnOldOpenedArchetype) {
        DependsOnOldOpenedArchetype = true;

        // The users of this candidate are new candidates.
        for (auto Use : result->getUses()) {
          usersToHandle.pushIfNotVisited(Use->getUser());
        }
      }
    }

    // No need to clone if there is no dependency on the old opened archetype.
    if (!DependsOnOldOpenedArchetype)
      continue;

    Builder.setInsertionPoint(user);
    auto NewI = Cloner.clone(user);
    // Result types of candidate's uses instructions may be using this archetype.
    // Thus, we need to try to replace it there.
    user->replaceAllUsesPairwiseWith(NewI);
    eraseFromParentWithDebugInsts(user);
  }
  return true;
}

/// Returns true if \p ai is a call to a lazy property getter, which we can
/// handle.
static bool isLazyPropertyGetter(ApplyInst *ai) {
  SILFunction *callee = ai->getReferencedFunctionOrNull();
  if (!callee || callee->isExternalDeclaration() ||
      !callee->isLazyPropertyGetter())
    return false;

  // We cannot inline a non-ossa function into an ossa function
  if (ai->getFunction()->hasOwnership() && !callee->hasOwnership())
    return false;

  // Only handle classes, but not structs.
  // Lazy property getters of structs have an indirect inout self parameter.
  // We don't know if the whole struct is overwritten between two getter calls.
  // In such a case, the lazy property could be reset to an Optional.none.
  // TODO: We could check this case with AliasAnalysis.
  if (ai->getArgument(0)->getType().isAddress())
    return false;

  // Check if the first block has a switch_enum of an Optional.
  // We don't handle getters of generic types, which have a switch_enum_addr.
  // This will be obsolete with opaque values anyway.
  auto *SEI = dyn_cast<SwitchEnumInst>(callee->getEntryBlock()->getTerminator());
  if (!SEI)
    return false;

  ASTContext &ctxt = SEI->getFunction()->getModule().getASTContext();
  EnumElementDecl *someDecl = ctxt.getOptionalSomeDecl();

  for (unsigned i = 0, e = SEI->getNumCases(); i != e; ++i) {
    auto Entry = SEI->getCase(i);
    if (Entry.first == someDecl) {
      SILBasicBlock *destBlock = Entry.second;
      return destBlock->getNumArguments() == 1;
    }
  }
  return false;
}

bool CSE::processNode(DominanceInfoNode *Node) {
  SILBasicBlock *BB = Node->getBlock();
  bool Changed = false;

  // See if any instructions in the block can be eliminated.  If so, do it.  If
  // not, add them to AvailableValues. Assume the block terminator can't be
  // erased.
  for (SILBasicBlock::iterator nextI = BB->begin(), E = BB->end();
       nextI != E;) {
    SILInstruction *Inst = &*nextI;
    ++nextI;

    LLVM_DEBUG(llvm::dbgs() << "SILCSE VISITING: " << *Inst << "\n");

    // Dead instructions should just be removed.
    if (isInstructionTriviallyDead(Inst)) {
      LLVM_DEBUG(llvm::dbgs() << "SILCSE DCE: " << *Inst << '\n');
      nextI = eraseFromParentWithDebugInsts(Inst);
      Changed = true;
      ++NumSimplify;
      continue;
    }

    // If the instruction can be simplified (e.g. X+0 = X) then replace it with
    // its simpler value.
    InstModCallbacks callbacks;
    nextI = simplifyAndReplaceAllSimplifiedUsesAndErase(Inst, callbacks,
                                                        &DeadEndBBs);
    if (callbacks.hadCallbackInvocation()) {
      ++NumSimplify;
      Changed = true;
      continue;
    }

    // If this is not a simple instruction that we can value number, skip it.
    if (!canHandle(Inst))
      continue;

    // If an instruction can be handled here, then it must also be handled
    // in isIdenticalTo, otherwise looking up a key in the map with fail to
    // match itself.
    assert(Inst->isIdenticalTo(Inst) &&
           "Inst must match itself for map to work");
    assert(llvm::DenseMapInfo<SimpleValue>::isEqual(Inst, Inst) &&
           "Inst must match itself for map to work");

    // Now that we know we have an instruction we understand see if the
    // instruction has an available value.  If so, use it.
    if (SILInstruction *AvailInst = AvailableValues->lookup(Inst)) {
      LLVM_DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << "  to: "
                              << *AvailInst << '\n');

      auto *AI = dyn_cast<ApplyInst>(Inst);
      if (AI && isLazyPropertyGetter(AI)) {
        // We do the actual transformation for lazy property getters later. It
        // changes the CFG and we don't want to disturb the dominator tree walk
        // here.
        lazyPropertyGetters.push_back(AI);
        continue;
      }
      if (!isa<OpenExistentialRefInst>(Inst) ||
          processOpenExistentialRef(cast<OpenExistentialRefInst>(Inst),
                                    cast<OpenExistentialRefInst>(AvailInst))) {
        if (Inst->getResults().empty()) {
          nextI = std::next(Inst->getIterator());
          Inst->eraseFromParent();
          Changed = true;
          ++NumCSE;
          continue;
        }
        if (!Inst->getFunction()->hasOwnership()) {
          Inst->replaceAllUsesPairwiseWith(AvailInst);
          nextI = std::next(Inst->getIterator());
          Inst->eraseFromParent();
          Changed = true;
          ++NumCSE;
          continue;
        }
        // TODO: Support MultipleValueInstructionResult in OSSA RAUW utility and
        // extend it here as well
        if (!isa<SingleValueInstruction>(Inst))
          continue;

        auto oldValue = cast<SingleValueInstruction>(Inst);
        auto newValue = cast<SingleValueInstruction>(AvailInst);
        OwnershipRAUWHelper helper(RAUWFixupContext, oldValue, newValue);
        // If RAUW requires cloning the original, then there's no point. If it
        // also requires introducing a copy and new borrow scope, then it's a
        // very bad idea.
        if (!helper.isValid() || helper.requiresCopyBorrowAndClone() ||
            helper.mayIntroduceUnoptimizableCopies())
          continue;
        // Replace SingleValueInstruction using OSSA RAUW here
        nextI = helper.perform();
        Changed = true;
        ++NumCSE;
        continue;
      }
    }

    // Otherwise, just remember that this value is available.
    AvailableValues->insert(Inst, Inst);
    LLVM_DEBUG(llvm::dbgs() << "SILCSE Adding to value table: " << *Inst
                            << " -> " << *Inst << "\n");
  }

  return Changed;
}

bool CSE::canHandle(SILInstruction *Inst) {
  if (auto *AI = dyn_cast<ApplyInst>(Inst)) {
    if (!AI->mayReadOrWriteMemory())
      return true;
    
    if (RunsOnHighLevelSil) {
      ArraySemanticsCall SemCall(AI);
      switch (SemCall.getKind()) {
        case ArrayCallKind::kGetCount:
        case ArrayCallKind::kGetCapacity:
        case ArrayCallKind::kCheckIndex:
        case ArrayCallKind::kCheckSubscript:
          return SemCall.hasGuaranteedSelf();
        default:
          return false;
      }
    }

    if (!AI->getFunction()->hasOwnership()) {
      // In non-OSSA we don't balance CSE'd apply results which return an
      // owned value.
      for (const SILResultInfo &ri : AI->getSubstCalleeType()->getResults()) {
        if (ri.getConvention() != ResultConvention::Unowned)
          return false;
      }
    }

    // We can CSE function calls which do not read or write memory and don't
    // have any other side effects.
    // Note that the function also may not contain any retains. And there are
    // functions which are read-none and have a retain, e.g. functions which
    // _convert_ a global_addr to a reference and retain it.
    auto MB = BCA->getMemoryBehavior(FullApplySite(AI), /*observeRetains*/false);
    if (MB == MemoryBehavior::None)
      return true;
    
    if (isLazyPropertyGetter(AI))
      return true;
      
    if (SILFunction *callee = AI->getReferencedFunctionOrNull()) {
      if (callee->isGlobalInit())
        return true;
    }
    
    return false;
  }
  if (auto *BI = dyn_cast<BuiltinInst>(Inst)) {
    switch (BI->getBuiltinInfo().ID) {
    case BuiltinValueKind::OnFastPath:
      // Although the onFastPath builtin has no side-effects we don't want to
      // (re-)move it.
      return false;
    case BuiltinValueKind::Once:
    case BuiltinValueKind::OnceWithContext:
      return true;
    default:
      return !BI->mayReadOrWriteMemory();
    }
  }
  if (auto *EMI = dyn_cast<ExistentialMetatypeInst>(Inst)) {
    return !EMI->getOperand()->getType().isAddress();
  }
  switch (Inst->getKind()) {
  case SILInstructionKind::ClassMethodInst:
  case SILInstructionKind::SuperMethodInst:
  case SILInstructionKind::FunctionRefInst:
  case SILInstructionKind::GlobalAddrInst:
  case SILInstructionKind::IntegerLiteralInst:
  case SILInstructionKind::FloatLiteralInst:
  case SILInstructionKind::StringLiteralInst:
  case SILInstructionKind::StructInst:
  case SILInstructionKind::StructExtractInst:
  case SILInstructionKind::StructElementAddrInst:
  case SILInstructionKind::TupleInst:
  case SILInstructionKind::TupleExtractInst:
  case SILInstructionKind::TupleElementAddrInst:
  case SILInstructionKind::MetatypeInst:
  case SILInstructionKind::ValueMetatypeInst:
  case SILInstructionKind::ObjCProtocolInst:
  case SILInstructionKind::RefElementAddrInst:
  case SILInstructionKind::RefTailAddrInst:
  case SILInstructionKind::ProjectBoxInst:
  case SILInstructionKind::IndexRawPointerInst:
  case SILInstructionKind::IndexAddrInst:
  case SILInstructionKind::PointerToAddressInst:
  case SILInstructionKind::AddressToPointerInst:
  case SILInstructionKind::CondFailInst:
  case SILInstructionKind::EnumInst:
  case SILInstructionKind::UncheckedEnumDataInst:
  case SILInstructionKind::UncheckedTrivialBitCastInst:
  case SILInstructionKind::UncheckedBitwiseCastInst:
  case SILInstructionKind::RefToRawPointerInst:
  case SILInstructionKind::RawPointerToRefInst:
  case SILInstructionKind::UpcastInst:
  case SILInstructionKind::ThickToObjCMetatypeInst:
  case SILInstructionKind::ObjCToThickMetatypeInst:
  case SILInstructionKind::UncheckedRefCastInst:
  case SILInstructionKind::UncheckedAddrCastInst:
  case SILInstructionKind::ObjCMetatypeToObjectInst:
  case SILInstructionKind::ObjCExistentialMetatypeToObjectInst:
  case SILInstructionKind::SelectEnumInst:
  case SILInstructionKind::RefToBridgeObjectInst:
  case SILInstructionKind::BridgeObjectToRefInst:
  case SILInstructionKind::BridgeObjectToWordInst:
  case SILInstructionKind::ClassifyBridgeObjectInst:
  case SILInstructionKind::ValueToBridgeObjectInst:
  case SILInstructionKind::MarkDependenceInst:
  case SILInstructionKind::InitExistentialMetatypeInst:
  case SILInstructionKind::WitnessMethodInst:
  case SILInstructionKind::ScalarPackIndexInst:
  case SILInstructionKind::DynamicPackIndexInst:
  case SILInstructionKind::TuplePackElementAddrInst:
  case SILInstructionKind::TypeValueInst:
    // Intentionally we don't handle (prev_)dynamic_function_ref.
    // They change at runtime.
#define LOADABLE_REF_STORAGE(Name, ...) \
  case SILInstructionKind::RefTo##Name##Inst: \
  case SILInstructionKind::Name##ToRefInst:
#include "swift/AST/ReferenceStorage.def"
    return true;
    // TODO: open_existential_ref is not handled in OSSA currently
    // This is because it is non trivial to ownership rauw copy_value users of a
    // redundant open_existential_ref. Suppose we have an `open_existential_ref`
    // and we are trying to replace it with another `open_existential_ref` of
    // different type. If one of the users of the old `open_existential_ref` is
    // a `copy_value`, we cannot just replace the use. Because `copy_value`'s
    // result type will be the old type as well.
    //
    // In all other places in the compiler where such a rauw needs to be
    // handled, a remapping type is initialized in the cloner and the
    // instruction is cloned before providing to the rauw utility. This would
    // correctly replace all the result types. But since copy_value does not
    // have type dependent operands, we cannot handle it in a similar way.
    //
    // This is currently a TODO until we can implement a clean way to fix this
    // issue.
  case SILInstructionKind::OpenExistentialRefInst:
    return !Inst->getFunction()->hasOwnership();
  default:
    return false;
  }
}

using ApplyWitnessPair = std::pair<ApplyInst *, WitnessMethodInst *>;

/// Returns the Apply and WitnessMethod instructions that use the
/// open_existential_addr instructions, or null if at least one of the
/// instructions is missing.
static ApplyWitnessPair getOpenExistentialUsers(OpenExistentialAddrInst *OE) {
  ApplyInst *AI = nullptr;
  WitnessMethodInst *WMI = nullptr;
  ApplyWitnessPair Empty = std::make_pair(nullptr, nullptr);

  for (auto *UI : getNonDebugUses(OE)) {
    auto *User = UI->getUser();
    if (!isa<WitnessMethodInst>(User) &&
        User->isTypeDependentOperand(UI->getOperandNumber()))
      continue;
    // Check that we have a single Apply user.
    if (auto *AA = dyn_cast<ApplyInst>(User)) {
      if (AI)
        return Empty;

      AI = AA;
      continue;
    }

    // Check that we have a single WMI user.
    if (auto *W = dyn_cast<WitnessMethodInst>(User)) {
      if (WMI)
        return Empty;

      WMI = W;
      continue;
    }

    // Unknown instruction.
    return Empty;
  }

  // Both instructions need to exist.
  if (!WMI || !AI)
    return Empty;

  // Make sure that the WMI and AI match.
  if (AI->getCallee() != WMI)
    return Empty;

  // We have exactly the pattern that we expected.
  return std::make_pair(AI, WMI);
}

/// Try to CSE the users of \p From to the users of \p To.
/// The original users of \p To are passed in ToApplyWitnessUsers.
/// Returns true on success.
static bool tryToCSEOpenExtCall(OpenExistentialAddrInst *From,
                                OpenExistentialAddrInst *To,
                                ApplyWitnessPair ToApplyWitnessUsers,
                                DominanceInfo *DA) {
  assert(From != To && "Can't replace instruction with itself");

  ApplyInst *FromAI = nullptr;
  ApplyInst *ToAI = nullptr;
  WitnessMethodInst *FromWMI = nullptr;
  WitnessMethodInst *ToWMI = nullptr;
  std::tie(FromAI, FromWMI) = getOpenExistentialUsers(From);
  std::tie(ToAI, ToWMI) = ToApplyWitnessUsers;

  // Make sure that the OEA instruction has exactly two expected users.
  if (!FromAI || !ToAI || !FromWMI || !ToWMI)
    return false;

  // Make sure we are calling the same method.
  if (FromWMI->getMember() != ToWMI->getMember())
    return false;

  // We are going to reuse the TO-WMI, so make sure it dominates the call site.
  if (!DA->properlyDominates(ToWMI, FromWMI))
    return false;

  SILBuilder Builder(FromAI);

  assert(FromAI->getArguments().size() == ToAI->getArguments().size() &&
         "Invalid number of arguments");

  // Don't handle any apply instructions that involve substitutions.
  if (ToAI->getSubstitutionMap().getReplacementTypes().size() != 1)
    return false;

  // Prepare the Apply args.
  SmallVector<SILValue, 8> Args;
  for (auto Op : FromAI->getArguments()) {
      Args.push_back(Op == From ? To : Op);
  }

  ApplyInst *NAI = Builder.createApply(ToAI->getLoc(), ToWMI,
                                       ToAI->getSubstitutionMap(), Args,
                                       ToAI->getApplyOptions());
  FromAI->replaceAllUsesWith(NAI);
  FromAI->eraseFromParent();
  ++NumOpenExtRemoved;
  return true;
}

/// Try to CSE the users of the protocol that's passed in argument \p Arg.
/// \returns True if some instructions were modified.
static bool CSExistentialInstructions(SILFunctionArgument *Arg,
                                      DominanceInfo *DA) {
  ParameterConvention Conv = Arg->getKnownParameterInfo().getConvention();
  // We can assume that the address of Proto does not alias because the
  // calling convention is In or In-guaranteed.
  bool MayAlias = Conv != ParameterConvention::Indirect_In_Guaranteed &&
                  Conv != ParameterConvention::Indirect_In;
  if (MayAlias)
    return false;

  // Now check that the only uses of the protocol are witness_method,
  // open_existential_addr and destroy_addr. Also, collect all of the 'opens'.
  llvm::SmallVector<OpenExistentialAddrInst*, 8> Opens;
  for (auto *UI : getNonDebugUses(Arg)) {
    auto *User = UI->getUser();
    if (auto *Open = dyn_cast<OpenExistentialAddrInst>(User)) {
      Opens.push_back(Open);
      continue;
    }

    if (isa<WitnessMethodInst>(User) || isa<DestroyAddrInst>(User))
      continue;

    // Bail out if we found an instruction that we can't handle.
    return false;
  }

  // Find the best dominating 'open' for each open existential.
  llvm::SmallVector<OpenExistentialAddrInst*, 8> TopDominator(Opens);

  bool Changed = false;

  // Try to CSE the users of the current open_existential_addr instruction with
  // one of the other open_existential_addr that dominate it.
  int NumOpenInstr = Opens.size();
  for (int i = 0; i < NumOpenInstr; ++i) {
    // Try to find a better dominating 'open' for the i-th instruction.
    OpenExistentialAddrInst *SomeOpen = TopDominator[i];
    for (int j = 0; j < NumOpenInstr; ++j) {

      if (i == j || TopDominator[i] == TopDominator[j])
        continue;

      OpenExistentialAddrInst *DominatingOpen = TopDominator[j];

      if (DominatingOpen->getOperand() != SomeOpen->getOperand())
        continue;

      if (DA->properlyDominates(DominatingOpen, SomeOpen)) {
        // We found an open instruction that DominatingOpen dominates:
        TopDominator[i] = TopDominator[j];
      }
    }
  }


  // Inspect all of the open_existential_addr instructions and record the
  // apply-witness users. We need to save the original Apply-Witness users
  // because we'll be adding new users and we need to make sure that we can
  // find the original users.
  llvm::SmallVector<ApplyWitnessPair, 8> OriginalAW;
  for (int i=0; i < NumOpenInstr; ++i) {
    OriginalAW.push_back(getOpenExistentialUsers(TopDominator[i]));
  }

  // Perform the CSE for the open_existential_addr instruction and their
  // dominating instruction.
  for (int i=0; i < NumOpenInstr; ++i) {
    if (Opens[i] != TopDominator[i])
      Changed |= tryToCSEOpenExtCall(Opens[i], TopDominator[i],
                                     OriginalAW[i], DA);
  }

  return Changed;
}

/// Detect multiple calls to existential members and try to CSE the instructions
/// that perform the method lookup (the open_existential_addr and
/// witness_method):
///
/// open_existential_addr %0 : $*Pingable to $*@opened("1E467EB8-...")
/// witness_method $@opened("1E467EB8-...") Pingable, #Pingable.ping, %2
/// apply %3<@opened("1E467EB8-...") Pingable>(%2)
///
/// \returns True if some instructions were modified.
static bool CSEExistentialCalls(SILFunction *Func, DominanceInfo *DA) {
  bool Changed = false;
  for (auto *Arg : Func->getArgumentsWithoutIndirectResults()) {
    if (Arg->getType().isExistentialType()) {
      auto *FArg = cast<SILFunctionArgument>(Arg);
      Changed |= CSExistentialInstructions(FArg, DA);
    }
  }

  return Changed;
}

namespace {
class SILCSE : public SILFunctionTransform {
  
  /// True if CSE is done on high-level SIL, i.e. semantic calls are not inlined
  /// yet. In this case some semantic calls can be CSEd.
  /// We only CSE semantic calls on high-level SIL because we can be sure that
  /// e.g. an Array as SILValue is really immutable (including its content).
  bool RunsOnHighLevelSil;
  
  void run() override {
    LLVM_DEBUG(llvm::dbgs() << "***** CSE on function: "
                            << getFunction()->getName() << " *****\n");

    DominanceAnalysis* DA = getAnalysis<DominanceAnalysis>();

    auto *BCA = PM->getAnalysis<BasicCalleeAnalysis>();
    SILOptFunctionBuilder FuncBuilder(*this);

    auto *Fn = getFunction();
    DeadEndBlocks DeadEndBBs(Fn);
    InstModCallbacks callbacks;
    OwnershipFixupContext FixupCtx{callbacks, DeadEndBBs};
    CSE C(RunsOnHighLevelSil, BCA, FuncBuilder, DeadEndBBs, FixupCtx);
    bool Changed = false;

    // Perform the traditional CSE.
    Changed |= C.processFunction(*Fn, DA->get(Fn));

    // Perform CSE of existential and witness_method instructions.
    Changed |= CSEExistentialCalls(Fn, DA->get(Fn));

    // Handle calls to lazy property getters, which are collected in
    // processFunction().
    if (C.processLazyPropertyGetters(*Fn)) {
      // Cleanup the dead blocks from the inlined lazy property getters.
      removeUnreachableBlocks(*Fn);
      invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
    } else if (Changed) {
      invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
    }
  }

public:
  SILCSE(bool RunsOnHighLevelSil) : RunsOnHighLevelSil(RunsOnHighLevelSil) {}
};
} // end anonymous namespace

SILTransform *swift::createCSE() {
  return new SILCSE(false);
}

SILTransform *swift::createHighLevelCSE() {
  return new SILCSE(true);
}
