//===- DeadFunctionElimination.cpp - Eliminate dead functions and globals -===//
//
// 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-dead-function-elimination"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/PatternMatch.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
using namespace swift;

STATISTIC(NumDeadFunc, "Number of dead functions eliminated");
STATISTIC(NumDeadGlobals, "Number of dead global variables eliminated");

namespace {

class DeadFunctionAndGlobalElimination {
  /// Represents a function which is implementing a vtable or witness table
  /// method.
  struct FuncImpl {
    FuncImpl(SILFunction *F, ClassDecl *Cl) : F(F), Impl(Cl) {}
    FuncImpl(SILFunction *F, ProtocolConformance *C) : F(F), Impl(C) {}

    /// The implementing function.
    SILFunction *F;

    /// This is a class decl if we are tracking a class_method (i.e. a vtable
    /// method) and a protocol conformance if we are tracking a witness_method.
    PointerUnion<ClassDecl *, ProtocolConformance*> Impl;
  };

  /// Stores which functions implement a vtable or witness table method.
  struct MethodInfo {

    MethodInfo(bool isWitnessMethod) :
      methodIsCalled(false), isWitnessMethod(isWitnessMethod) {}

    /// All functions which implement the method. Together with the class for
    /// which the function implements the method. In case of a witness method,
    /// the class pointer is null.
    SmallVector<FuncImpl, 8> implementingFunctions;

    /// True, if the method is called, meaning that any of it's implementations
    /// may be called.
    bool methodIsCalled;

    /// True if this is a witness method, false if it's a vtable method.
    bool isWitnessMethod;

    /// Adds an implementation of the method in a specific class.
    void addClassMethodImpl(SILFunction *F, ClassDecl *C) {
      assert(!isWitnessMethod);
      implementingFunctions.push_back(FuncImpl(F, C));
    }

    /// Adds an implementation of the method in a specific conformance.
    ///
    /// \p Conf is null for default implementations and move-only deinits
    void addWitnessFunction(SILFunction *F, ProtocolConformance *Conf) {
      assert(isWitnessMethod);
      implementingFunctions.push_back(FuncImpl(F, Conf));
    }
  };

  SILModule *Module;

  llvm::DenseMap<AbstractFunctionDecl *, MethodInfo *> MethodInfos;
  llvm::SpecificBumpPtrAllocator<MethodInfo> MethodInfoAllocator;

  llvm::SmallSetVector<SILFunction *, 16> Worklist;

  llvm::SmallPtrSet<void *, 32> AliveFunctionsAndTables;

  bool keepExternalWitnessTablesAlive;
  bool keepStringSwitchIntrinsicAlive;

  /// Checks is a function is alive, e.g. because it is visible externally.
  bool isAnchorFunction(SILFunction *F) {
    // In embedded Swift, (even public) generic functions *after serialization*
    // cannot be used externally and are not anchors.
    bool embedded = Module->getOptions().EmbeddedSwift;
    bool generic = F->isGeneric();
    bool isSerialized = Module->isSerialized();
    if (embedded && generic && isSerialized)
      return false;

    // Functions that may be used externally cannot be removed.
    if (F->isPossiblyUsedExternally())
      return true;

    // Distributed functions are looked up by name at runtime via the
    // accessible-function registry. Distributed thunks (TWTE) are dispatched
    // through the witness table by the distributed accessor. Neither kind
    // is necessarily referenced from SIL, so both must be kept alive.
    if (F->isDistributed() || F->isThunk() == IsDistributedThunk)
      return true;

    // The recipient-side `$distributedProxyAdapter$<base>` thunk is
    // referenced by name from the IRGen-only distributed-target accessor;
    // it has no SIL caller. DFE must not remove it.
    if (F->isThunk() == IsDistributedProxyAdapterThunk)
      return true;

    if (F->getDynamicallyReplacedFunction())
      return true;

    if (F->isDynamicallyReplaceable())
      return true;

    if (F->getReferencedAdHocRequirementWitnessFunction())
      return true;

    // Don't remove pre-specialized functions. We need to preserver the
    // pre-specialization specifications from other modules.
    if (F->hasPrespecialization())
      return true;

    // ObjC functions are called through the runtime and are therefore alive
    // even if not referenced inside SIL.
    if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod)
      return true;

    // To support ObjectOutliner's replacing of calls to findStringSwitchCase
    // with _findStringSwitchCaseWithCache. In Embedded Swift, we have to load
    // the body of this function early and specialize it, so that ObjectOutliner
    // can reference it later. To make this work we have to avoid DFE'ing it in
    // the early DFE pass. Late DFE will take care of it if actually unused.
    if (keepStringSwitchIntrinsicAlive &&
        F->hasSemanticsAttr("findStringSwitchCaseWithCache"))
      return true;

    return false;
  }

  /// Gets or creates the MethodInfo for a vtable or witness table method.
  /// \p decl The method declaration. In case of a vtable method this is always
  ///         the most overridden method.
  MethodInfo *getMethodInfo(AbstractFunctionDecl *decl, bool isWitnessMethod) {
    MethodInfo *&entry = MethodInfos[decl];
    if (entry == nullptr) {
      entry = new (MethodInfoAllocator.Allocate()) MethodInfo(isWitnessMethod);
    }
    assert(entry->isWitnessMethod == isWitnessMethod);
    return entry;
  }

  /// Returns true if a function is marked as alive.
  bool isAlive(SILFunction *F) {
    return AliveFunctionsAndTables.count(F) != 0;
  }

  /// Returns true if a witness table is marked as alive.
  bool isAlive(SILWitnessTable *WT) {
    return AliveFunctionsAndTables.count(WT) != 0;
  }

  /// Returns true if a global variable is marked as alive.
  bool isAlive(SILGlobalVariable *global) {
    return AliveFunctionsAndTables.count(global) != 0;
  }

  /// Marks a function as alive.
  void makeAlive(SILFunction *F) {
    LLVM_DEBUG(llvm::dbgs() << "         makeAlive " << F->getName() << '\n');
    AliveFunctionsAndTables.insert(F);
    assert(F && "function does not exist");
    Worklist.insert(F);
  }

  /// Marks all contained functions and witness tables of a witness table as
  /// alive.
  void makeAlive(SILWitnessTable *WT) {
    if (isAvailableExternally(WT->getLinkage()) &&
        !keepExternalWitnessTablesAlive) {
      return;
    }
  
    LLVM_DEBUG(llvm::dbgs() << "    scan witness table " << WT->getName()
                            << '\n');

    AliveFunctionsAndTables.insert(WT);
    for (const SILWitnessTable::Entry &entry : WT->getEntries()) {
      switch (entry.getKind()) {
        case SILWitnessTable::Method: {

          auto methodWitness = entry.getMethodWitness();
          auto *fd = cast<AbstractFunctionDecl>(methodWitness.Requirement.
                                                getDecl());
          assert(fd == getBaseMethod(fd) &&
                 "key in witness table is overridden");
          SILFunction *F = methodWitness.Witness;
          if (F) {
            MethodInfo *MI = getMethodInfo(fd, /*isWitnessMethod*/ true);
            if (MI->methodIsCalled || !F->isDefinition()) {
              ensureAlive(F);
            } else if (fd->isDistributed()
                       || F->isDistributed()
                       || F->isThunk() == IsDistributedThunk) {
              // Distributed method witnesses must be kept alive.
              // Distributed function accessors dispatch through the witness
              // table at runtime, which is invisible to static call-site
              // analysis. So if we wouldn't keep alive the func explicitly here,
              // the witness thunk can be eliminated (especially with -O + WMO),
              // and IRGen replaces it with swift_deletedAsyncMethodError.
              ensureAlive(F);
            }
          }
        } break;

        case SILWitnessTable::AssociatedConformance:
        case SILWitnessTable::BaseProtocol:
        case SILWitnessTable::Invalid:
        case SILWitnessTable::AssociatedType:
          break;
      }
    }
  }

  /// Marks the \p global and all functions, which are referenced from its
  /// initializer as alive.
  void makeAlive(SILGlobalVariable *global) {
    AliveFunctionsAndTables.insert(global);
    for (const SILInstruction &initInst : *global) {
      if (auto *fRef = dyn_cast<FunctionRefInst>(&initInst))
        ensureAlive(fRef->getReferencedFunction());
      if (auto *gRef = dyn_cast<GlobalAddrInst>(&initInst))
        ensureAlive(gRef->getReferencedGlobal());
      if (auto *gVal = dyn_cast<GlobalValueInst>(&initInst))
        ensureAlive(gVal->getReferencedGlobal());
    }
  }

  /// Marks the declarations referenced by a key path pattern as alive if they
  /// aren't yet.
  void
  ensureKeyPathComponentIsAlive(const KeyPathPatternComponent &component) {
    component.visitReferencedFunctionsAndMethods(
      [this](SILFunction *F) {
       ensureAlive(F);
      },
      [this](SILDeclRef method) {
        if (method.isForeign) {
          // Nothing to do here: foreign functions aren't ours to be deleting.
          // (And even if they were, they're ObjC-dispatched and thus anchored
          // already: see isAnchorFunction)
        } else {
          auto decl = cast<AbstractFunctionDecl>(method.getDecl());
          if (auto clazz = dyn_cast<ClassDecl>(decl->getDeclContext())) {
            ensureAliveClassMethod(getMethodInfo(decl, /*witness*/ false),
                                   dyn_cast<FuncDecl>(decl),
                                   clazz);
          } else if (isa<ProtocolDecl>(decl->getDeclContext())) {
            ensureAliveProtocolMethod(getMethodInfo(decl, /*witness*/ true));
          } else {
            llvm_unreachable("key path keyed by a non-class, non-protocol method");
          }
        }
      }
    );
  }

  /// Marks a function as alive if it is not alive yet.
  void ensureAlive(SILFunction *F) {
    if (!isAlive(F))
      makeAlive(F);
  }

  /// Marks a global variable as alive if it is not alive yet.
  void ensureAlive(SILGlobalVariable *global) {
    if (!isAlive(global))
      makeAlive(global);
  }

  /// Returns true if the implementation of method \p FD in class \p ImplCl
  /// may be called when the type of the class_method's operand is \p MethodCl.
  /// Both, \p MethodCl and \p ImplCl, may by null if not known or if it's a
  /// protocol method.
  static bool canHaveSameImplementation(FuncDecl *FD, ClassDecl *MethodCl,
                                        ClassDecl *ImplCl) {
    if (!FD || !MethodCl || !ImplCl)
      return true;

    // All implementations of derived classes may be called.
    if (MethodCl->isSuperclassOf(ImplCl))
      return true;

    // Check if the method implementation is the same in a super class, i.e.
    // it is not overridden in the derived class.
    auto *Impl1 = MethodCl->findImplementingMethod(FD);
    assert(Impl1);
    auto *Impl2 = ImplCl->findImplementingMethod(FD);
    assert(Impl2);

    return Impl1 == Impl2;
  }

  /// Marks the implementing functions of the method \p FD as alive. If it is a
  /// class method, \p MethodCl is the type of the class_method instruction's
  /// operand.
  void ensureAliveClassMethod(MethodInfo *mi, FuncDecl *FD, ClassDecl *MethodCl) {
    if (mi->methodIsCalled)
      return;
    bool allImplsAreCalled = true;

    for (FuncImpl &FImpl : mi->implementingFunctions) {
      if (!isAlive(FImpl.F) &&
          canHaveSameImplementation(FD, MethodCl,
                                    cast<ClassDecl *>(FImpl.Impl))) {
        ensureAlive(FImpl.F);
      } else {
        allImplsAreCalled = false;
      }
    }
    if (allImplsAreCalled)
      mi->methodIsCalled = true;
  }

  /// Marks the implementing functions of the protocol method \p mi as alive.
  void ensureAliveProtocolMethod(MethodInfo *mi) {
    assert(mi->isWitnessMethod);
    if (mi->methodIsCalled)
      return;
    mi->methodIsCalled = true;
    for (FuncImpl &FImpl : mi->implementingFunctions) {
      if (auto Conf = FImpl.Impl.dyn_cast<ProtocolConformance *>()) {
        SILWitnessTable *WT = Module->lookUpWitnessTable(Conf);
        if (!WT || isAlive(WT))
          ensureAlive(FImpl.F);
      } else {
        ensureAlive(FImpl.F);
      }
    }
  }

  /// Scans all references inside a function.
  void scanFunction(SILFunction *F) {

    LLVM_DEBUG(llvm::dbgs() << "    scan function " << F->getName() << '\n');

    if (auto *replacedFn = F->getDynamicallyReplacedFunction())
      ensureAlive(replacedFn);

    if (auto *adHocWitness = F->getReferencedAdHocRequirementWitnessFunction())
      ensureAlive(adHocWitness);

    // First scan all instructions of the function.
    for (SILBasicBlock &BB : *F) {
      for (SILInstruction &I : BB) {
        if (auto *WMI = dyn_cast<WitnessMethodInst>(&I)) {
          auto *funcDecl = getBaseMethod(
              cast<AbstractFunctionDecl>(WMI->getMember().getDecl()));
          MethodInfo *mi = getMethodInfo(funcDecl, /*isWitnessTable*/ true);
          ensureAliveProtocolMethod(mi);
        } else if (auto *MI = dyn_cast<MethodInst>(&I)) {
          auto *funcDecl = getBaseMethod(
              cast<AbstractFunctionDecl>(MI->getMember().getDecl()));
          assert(MI->getNumOperands() - MI->getNumTypeDependentOperands() == 1
                 && "method insts except witness_method must have 1 operand");
          ClassDecl *MethodCl = MI->getOperand(0)->getType().
                                  getClassOrBoundGenericClass();
          MethodInfo *mi = getMethodInfo(funcDecl, /*isWitnessTable*/ false);
          ensureAliveClassMethod(mi, dyn_cast<FuncDecl>(funcDecl), MethodCl);
        } else if (auto *FRI = dyn_cast<FunctionRefInst>(&I)) {
          ensureAlive(FRI->getReferencedFunction());
        } else if (auto *FRI = dyn_cast<DynamicFunctionRefInst>(&I)) {
          ensureAlive(FRI->getInitiallyReferencedFunction());
        } else if (auto *FRI = dyn_cast<PreviousDynamicFunctionRefInst>(&I)) {
          ensureAlive(FRI->getInitiallyReferencedFunction());
        } else if (auto *KPI = dyn_cast<KeyPathInst>(&I)) {
          for (auto &component : KPI->getPattern()->getComponents())
            ensureKeyPathComponentIsAlive(component);
        } else if (auto *GA = dyn_cast<GlobalAddrInst>(&I)) {
          ensureAlive(GA->getReferencedGlobal());
        } else if (auto *agi = dyn_cast<AllocGlobalInst>(&I)) {
          ensureAlive(agi->getReferencedGlobal());
        } else if (auto *GV = dyn_cast<GlobalValueInst>(&I)) {
          ensureAlive(GV->getReferencedGlobal());
        } else if (auto *HSI = dyn_cast<HasSymbolInst>(&I)) {
          SmallVector<SILFunction *, 4> fns;
          HSI->getReferencedFunctions(fns);
          for (auto fn : fns) {
            ensureAlive(fn);
          }
        }
      }
    }
  }

  /// Retrieve the visibility information from the AST.
  ///
  /// This differs from SILModule::isVisibleExternally(VarDecl *) because of
  /// it's handling of class methods. It returns true for methods whose
  /// declarations are not directly visible externally, but have been imported
  /// from another module. This ensures that entries aren't deleted from vtables
  /// imported from the stdlib.
  /// FIXME: Passes should not embed special logic for handling linkage.
  bool isVisibleExternally(const ValueDecl *decl) {
    AccessLevel access = decl->getEffectiveAccess();
    SILLinkage linkage;
    switch (access) {
    case AccessLevel::Private:
    case AccessLevel::FilePrivate:
      linkage = SILLinkage::Private;
      break;
    case AccessLevel::Internal:
      linkage = SILLinkage::Hidden;
      break;
    case AccessLevel::Package:
      linkage = SILLinkage::Package;
      break;
    case AccessLevel::Public:
    case AccessLevel::Open:
      linkage = SILLinkage::Public;
      break;
    }
    if (isPossiblyUsedExternally(linkage, Module->isWholeModule()))
      return true;

    // Special case for vtable visibility.
    if (decl->getDeclContext()->getParentModule() != Module->getSwiftModule())
      return true;

    return false;
  }

  /// Find all functions which are alive from the beginning.
  /// For example, functions which may be referenced externally.
  void findAnchors() {

    findAnchorsInTables();

    for (SILFunction &F : *Module) {
      if (isAnchorFunction(&F)) {
        LLVM_DEBUG(llvm::dbgs() << "  anchor function: " << F.getName() <<"\n");
        ensureAlive(&F);
      }

      // Make sure that functions referenced by _specialize(target: targetFun())
      // are kept alive.
      F.forEachSpecializeAttrTargetFunction(
          [this](SILFunction *targetFun) { ensureAlive(targetFun); });

      bool retainBecauseFunctionIsNoOpt = !F.shouldOptimize();
      if (Module->getOptions().EmbeddedSwift)
        retainBecauseFunctionIsNoOpt = false;

      if (retainBecauseFunctionIsNoOpt) {
        LLVM_DEBUG(llvm::dbgs() << "  anchor a no optimization function: "
                                << F.getName() << "\n");
        ensureAlive(&F);
      }
    }
    
    for (SILGlobalVariable &global : Module->getSILGlobals()) {
      if (global.isPossiblyUsedExternally())
        ensureAlive(&global);
    }
  }

  /// The main entry point of the optimization.
  bool findAliveFunctions() {

    LLVM_DEBUG(llvm::dbgs() << "running function elimination\n");

    // Find everything which may not be eliminated, e.g. because it is accessed
    // externally.
    findAnchors();

    // The core of the algorithm: Mark functions as alive which can be reached
    // from the anchors.
    while (!Worklist.empty()) {
      SILFunction *F = Worklist.back();
      Worklist.pop_back();
      scanFunction(F);
    }

    return false;
  }

  void collectMethodImplementations() {
    // Collect vtable method implementations.
    for (auto &vTable : Module->getVTables()) {
      for (const SILVTable::Entry &entry : vTable->getEntries()) {
        // We don't need to collect destructors because we mark them as alive
        // anyway.
        if (entry.getMethod().kind == SILDeclRef::Kind::Deallocator ||
            entry.getMethod().kind == SILDeclRef::Kind::IVarDestroyer) {
          continue;
        }
        SILFunction *F = entry.getImplementation();
        auto *fd = getBaseMethod(
            cast<AbstractFunctionDecl>(entry.getMethod().getDecl()));
        MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ false);
        mi->addClassMethodImpl(F, vTable->getClass());
      }
    }

    // Collect witness method implementations.
    for (SILWitnessTable &WT : Module->getWitnessTableList()) {
      auto Conf = WT.getConformance();
      for (const SILWitnessTable::Entry &entry : WT.getEntries()) {
        if (entry.getKind() != SILWitnessTable::Method)
          continue;

        auto methodWitness = entry.getMethodWitness();
        auto *fd = cast<AbstractFunctionDecl>(methodWitness.Requirement.
                                              getDecl());
        assert(fd == getBaseMethod(fd) &&
               "key in witness table is overridden");
        SILFunction *F = methodWitness.Witness;
        if (!F)
          continue;

        MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
        mi->addWitnessFunction(F, Conf);
      }
    }

    // Collect default witness method implementations.
    for (SILDefaultWitnessTable &WT : Module->getDefaultWitnessTableList()) {
      for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
        if (!entry.isValid() || entry.getKind() != SILWitnessTable::Method)
          continue;

        SILFunction *F = entry. getMethodWitness().Witness;
        auto *fd = cast<AbstractFunctionDecl>(
                     entry.getMethodWitness().Requirement.getDecl());
        MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
        mi->addWitnessFunction(F, nullptr);
      }
    }
  }

  /// Take functions reachable via vtables and witness_tables into account
  /// when computing a function liveness information.
  void findAnchorsInTables() {

    collectMethodImplementations();

    // Check vtable methods.
    for (auto &vTable : Module->getVTables()) {
      LLVM_DEBUG(llvm::dbgs() << " processing vtable "
                              << vTable->getClass()->getName() << '\n');
      for (const SILVTable::Entry &entry : vTable->getEntries()) {
        if (entry.getMethod().kind == SILDeclRef::Kind::Deallocator ||
            entry.getMethod().kind == SILDeclRef::Kind::IVarDestroyer) {
          // Destructors are alive because they are called from swift_release
          ensureAlive(entry.getImplementation());
          continue;
        }

        SILFunction *F = entry.getImplementation();
        auto *fd = getBaseMethod(
            cast<AbstractFunctionDecl>(entry.getMethod().getDecl()));

        // In Embedded Swift, we don't expect SILFunction without definitions on
        // vtable entries. Having one means the base method was DCE'd already,
        // so let's avoid marking it alive in the subclass vtable either.
        bool embedded = Module->getOptions().EmbeddedSwift;
        if (embedded && !F->isDefinition()) { continue; }
        
        if (// We also have to check the method declaration's access level.
            // Needed if it's a public base method declared in another
            // compilation unit (for this we have no SILFunction).
            isVisibleExternally(fd)
            || Module->isExternallyVisibleDecl(fd)
            // Declarations are always accessible externally, so they are alive.
            || !F->isDefinition()) {
          MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ false);
          ensureAliveClassMethod(mi, nullptr, nullptr);
        }
      }
    }

    // Check witness table methods.
    for (SILWitnessTable &WT : Module->getWitnessTableList()) {
      ProtocolConformance *Conf = WT.getConformance();
      bool tableExternallyVisible = isVisibleExternally(Conf->getProtocol());
      // The witness table is visible from "outside". Therefore all methods
      // might be called and we mark all methods as alive.
      for (const SILWitnessTable::Entry &entry : WT.getEntries()) {
        if (entry.getKind() != SILWitnessTable::Method)
          continue;

        auto methodWitness = entry.getMethodWitness();
        auto *fd = cast<AbstractFunctionDecl>(methodWitness.Requirement.
                                              getDecl());
        assert(fd == getBaseMethod(fd) &&
               "key in witness table is overridden");
        SILFunction *F = methodWitness.Witness;
        if (!F)
          continue;

        if (!tableExternallyVisible && !Module->isExternallyVisibleDecl(fd))
          continue;

        MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
        ensureAliveProtocolMethod(mi);
      }

      // We don't do dead witness table elimination right now. So we assume
      // that all witness tables are alive. Dead witness table elimination is
      // done in IRGen by lazily emitting witness tables.
      makeAlive(&WT);
    }

    // Check default witness methods.
    for (SILDefaultWitnessTable &WT : Module->getDefaultWitnessTableList()) {
      if (isVisibleExternally(WT.getProtocol())) {
        // The default witness table is visible from "outside". Therefore all
        // methods might be called and we mark all methods as alive.
        for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
          if (!entry.isValid() || entry.getKind() != SILWitnessTable::Method)
            continue;

          auto *fd =
              cast<AbstractFunctionDecl>(
                entry.getMethodWitness().Requirement.getDecl());
          assert(fd == getBaseMethod(fd) &&
                 "key in default witness table is overridden");
          SILFunction *F = entry.getMethodWitness().Witness;
          if (!F)
            continue;

          MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
          ensureAliveProtocolMethod(mi);
        }
      }
    }

    // Check default override tables.
    for (auto &table : Module->getDefaultOverrideTableList()) {
      for (auto &entry : table.getEntries()) {
        ensureAlive(entry.impl);
      }
    }

    // Check property descriptor implementations.
    for (SILProperty &P : Module->getPropertyList()) {
      if (auto component = P.getComponent()) {
        ensureKeyPathComponentIsAlive(*component);
      }
    }
    // Check differentiability witness entries.
    for (auto &dw : Module->getDifferentiabilityWitnessList()) {
      ensureAlive(dw.getOriginalFunction());
      if (dw.getJVP())
        ensureAlive(dw.getJVP());
      if (dw.getVJP())
        ensureAlive(dw.getVJP());
    }

    // Collect move-only deinit methods.
    //
    // TODO: Similar to addWitnessFunction, track the associated
    // struct/enum decl to allow DCE of unused deinits.
    for (auto *deinit : Module->getMoveOnlyDeinits()) {
      makeAlive(deinit->getImplementation());
    }
  }

  /// Removes all dead methods from vtables and witness tables.
  bool removeDeadEntriesFromTables() {
    bool changedTable = false;
    for (auto &vTable : Module->getVTables()) {
      vTable->removeEntries_if(
          [this, &changedTable](SILVTable::Entry &entry) -> bool {
            if (!isAlive(entry.getImplementation())) {
              LLVM_DEBUG(llvm::dbgs()
                         << "  erase dead vtable method "
                         << entry.getImplementation()->getName() << "\n");
              changedTable = true;
              return true;
            }
            return false;
          });
    }

    auto &WitnessTables = Module->getWitnessTableList();
    for (auto WI = WitnessTables.begin(), EI = WitnessTables.end(); WI != EI;) {
      SILWitnessTable *WT = &*WI;
      ++WI;
      WT->clearMethods_if([this, &changedTable]
                          (const SILWitnessTable::MethodWitness &MW) -> bool {
        if (!isAlive(MW.Witness)) {
          auto *fd = cast<AbstractFunctionDecl>(MW.Requirement.getDecl());
          // Distributed method witnesses must never be cleared: the
          // distributed accessor dispatches through the witness table at
          // runtime. This is a safety net; makeAlive() should already have
          // kept the witness alive. rdar://168881945
          if (fd->isDistributed()
              || MW.Witness->isDistributed()
              || MW.Witness->isThunk() == IsDistributedThunk) {
            LLVM_DEBUG(llvm::dbgs() << "  prevent dead code elimination for distributed/distributed_thunk "
                                    << MW.Witness->getName() << "\n");
            return false;
          }
          LLVM_DEBUG(llvm::dbgs() << "  erase dead witness method "
                                  << MW.Witness->getName() << "\n");
          changedTable = true;
          return true;
        }
        return false;
      });
    }

    auto DefaultWitnessTables = Module->getDefaultWitnessTables();
    for (auto WI = DefaultWitnessTables.begin(),
              EI = DefaultWitnessTables.end();
         WI != EI;) {
      SILDefaultWitnessTable *WT = &*WI;
      ++WI;
      WT->clearMethods_if([this, &changedTable](SILFunction *MW) -> bool {
        if (!MW)
          return false;
        if (!isAlive(MW)) {
          LLVM_DEBUG(llvm::dbgs() << "  erase dead default witness method "
                                  << MW->getName() << "\n");
          changedTable = true;
          return true;
        }
        return false;
      });
    }
    return changedTable;
  }

public:
  DeadFunctionAndGlobalElimination(SILModule *module,
                                   bool keepExternalWitnessTablesAlive,
                                   bool keepStringSwitchIntrinsicAlive) :
    Module(module),
    keepExternalWitnessTablesAlive(keepExternalWitnessTablesAlive),
    keepStringSwitchIntrinsicAlive(keepStringSwitchIntrinsicAlive) {}

  /// The main entry point of the optimization.
  void eliminateFunctionsAndGlobals(SILModuleTransform *DFEPass) {

    LLVM_DEBUG(llvm::dbgs() << "running dead function elimination\n");
    findAliveFunctions();

    bool changedTables = removeDeadEntriesFromTables();

    // First drop all references so that we don't get problems with non-zero
    // reference counts of dead functions.
    llvm::SmallVector<SILFunction *, 16> DeadFunctions;
    llvm::SmallVector<SILGlobalVariable *, 16> DeadGlobals;
    for (SILFunction &F : *Module) {
      if (!isAlive(&F)) {
        F.dropAllReferences();
        DeadFunctions.push_back(&F);
      }
    }
    
    for (SILGlobalVariable &global : Module->getSILGlobals()) {
      if (!isAlive(&global)) {
        global.dropAllReferences();
        DeadGlobals.push_back(&global);
      }
    }

    // Next step: delete dead witness tables.
    SILModule::WitnessTableListType &WTables = Module->getWitnessTableList();
    for (auto Iter = WTables.begin(), End = WTables.end(); Iter != End;) {
      SILWitnessTable *Wt = &*Iter;
      Iter++;
      if (!isAlive(Wt)) {
        LLVM_DEBUG(llvm::dbgs() << "  erase dead witness table "
                                << Wt->getName() << '\n');
        Module->deleteWitnessTable(Wt);
      }
    }

    // Last step: delete all dead functions.
    for (SILFunction *deadFunc : DeadFunctions) {
      LLVM_DEBUG(llvm::dbgs() << "  erase dead function " << deadFunc->getName()
                              << "\n");
      ++NumDeadFunc;
      DFEPass->notifyWillDeleteFunction(deadFunc);
      Module->eraseFunction(deadFunc);
    }
    for (SILGlobalVariable *deadGlobal : DeadGlobals) {
      ++NumDeadGlobals;
      Module->eraseGlobalVariable(deadGlobal);
    }
    
    if (changedTables)
      DFEPass->invalidateFunctionTables();
  }
};

} // end anonymous namespace

//===----------------------------------------------------------------------===//
//                      Pass Definition and Entry Points
//===----------------------------------------------------------------------===//

namespace {

class DeadFunctionAndGlobalEliminationPass : public SILModuleTransform {

private:
  bool isLateDFE;

public:
  DeadFunctionAndGlobalEliminationPass(bool isLateDFE) : isLateDFE(isLateDFE) {}

  void run() override {
    LLVM_DEBUG(llvm::dbgs() << "Running DeadFuncElimination\n");

    // The deserializer caches functions that it deserializes so that if it is
    // asked to deserialize that function again, it does not do extra work. This
    // causes the function's reference count to be incremented causing it to be
    // alive unnecessarily. We invalidate the SILLoaderCaches here so that we
    // can eliminate such functions.
    getModule()->invalidateSILLoaderCaches();

    DeadFunctionAndGlobalElimination deadFunctionElimination(
        getModule(),
        /*keepExternalWitnessTablesAlive*/ !isLateDFE,
        /*keepStringSwitchIntrinsicAlive*/ !isLateDFE);
    deadFunctionElimination.eliminateFunctionsAndGlobals(this);
  }
};

} // end anonymous namespace

SILTransform *swift::createDeadFunctionAndGlobalElimination() {
  return new DeadFunctionAndGlobalEliminationPass(/*isLateDFE*/ false);
}

SILTransform *swift::createLateDeadFunctionAndGlobalElimination() {
  return new DeadFunctionAndGlobalEliminationPass(/*isLateDFE*/ true);
}

void swift::performSILDeadFunctionElimination(SILModule *M) {
  llvm::SmallVector<PassKind, 1> Pass =
    {PassKind::DeadFunctionAndGlobalElimination};
  auto &opts = M->getOptions();
  auto plan = SILPassPipelinePlan::getPassPipelineForKinds(opts, Pass);
  executePassPipelinePlan(M, plan);
}
