//===--- InstOptUtils.h - SILOptimizer instruction utilities ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
///
/// Utilities used by the SILOptimizer for analyzing and transforming
/// SILInstructions.
///
/// SIL/InstUtils.h provides essential SILInstruction utilities.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H
#define SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H

#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SILOptimizer/Utils/InstModCallbacks.h"
#include "llvm/ADT/SmallPtrSet.h"

namespace swift {

class DominanceInfo;
class DeadEndBlocks;
class BasicCalleeAnalysis;
class DestructorAnalysis;
template <class T> class NullablePtr;

/// Transform a Use Range (Operand*) into a User Range (SILInstruction *)
using UserTransform = std::function<SILInstruction *(Operand *)>;
using ValueBaseUserRange =
    TransformRange<iterator_range<ValueBase::use_iterator>, UserTransform>;

template <typename Range>
inline TransformRange<Range, UserTransform> makeUserRange(Range range) {
  auto toUser = [](Operand *operand) { return operand->getUser(); };
  return makeTransformRange(range, UserTransform(toUser));
}

/// Transform a use_iterator range (Operand*) into an llvm::iterator_range
/// of users (SILInstruction *)
inline iterator_range<llvm::mapped_iterator<ValueBase::use_iterator, UserTransform>>
makeUserIteratorRange(iterator_range<ValueBase::use_iterator> useRange) {
  auto toUser = [](Operand *operand) { return operand->getUser(); };
  return llvm::map_range(useRange, UserTransform(toUser));
}

using DeadInstructionSet = llvm::SmallSetVector<SILInstruction *, 8>;

/// Create a retain of \p Ptr before the \p InsertPt.
NullablePtr<SILInstruction> createIncrementBefore(SILValue ptr,
                                                  SILInstruction *insertpt);

/// Create a release of \p Ptr before the \p InsertPt.
NullablePtr<SILInstruction> createDecrementBefore(SILValue ptr,
                                                  SILInstruction *insertpt);

/// Get the insertion point after \p val.
std::optional<SILBasicBlock::iterator> getInsertAfterPoint(SILValue val);

/// True if this instruction's only uses are debug_value (in -O mode),
/// destroy_value, end_lifetime or end-of-scope instruction such as end_borrow.
bool hasOnlyEndOfScopeOrEndOfLifetimeUses(SILInstruction *inst);

/// Return the number of @inout arguments passed to the given apply site.
unsigned getNumInOutArguments(FullApplySite applySite);

/// If \c inst is dead, delete it and recursively eliminate all code that
/// becomes dead because of that. If more than one instruction must
/// be checked/deleted use the \c InstructionDeleter utility.
///
/// This function will add necessary compensation code to fix the lifetimes of
/// the operands of the deleted instructions.
///
/// \pre the SIL function containing the instruction is assumed to be
/// consistent, i.e., does not have under or over releases.
///
/// \p callbacks.onDelete() is invoked to delete each instruction.
void eliminateDeadInstruction(SILInstruction *inst,
                              InstModCallbacks callbacks = InstModCallbacks());

/// For each of the given instructions, if they are dead delete them
/// along with their dead operands. Note this utility must be phased out and
/// replaced by \c eliminateDeadInstruction  and
/// \c InstructionDeleter utilities.
///
/// \param inst The ArrayRef of instructions to be deleted.
/// \param force If Force is set, don't check if the top level instructions
///        are considered dead - delete them regardless.
/// \param callbacks The inst mod callbacks used to delete instructions.
///
/// Deprecated: Use InstructionDeleter instead.
void recursivelyDeleteTriviallyDeadInstructions(
    ArrayRef<SILInstruction *> inst, bool force = false,
    InstModCallbacks callbacks = InstModCallbacks());

/// If the given instruction is dead, delete it along with its dead
/// operands. Note this utility must be phased out and replaced by
/// \c eliminateDeadInstruction and
/// \c InstructionDeleter utilities.
///
/// \param inst The instruction to be deleted.
/// \param force If Force is set, don't check if the top level instruction is
///        considered dead - delete it regardless.
/// \param callbacks InstModCallback used to delete instructions.
///
/// Deprecated: Use InstructionDeleter instead.
void recursivelyDeleteTriviallyDeadInstructions(
    SILInstruction *inst, bool force = false,
    InstModCallbacks callbacks = InstModCallbacks());

/// True if this instruction can be deleted if all its uses can also be deleted.
bool isInstructionTriviallyDeletable(SILInstruction *inst);

/// Perform a fast local check to see if the instruction is dead.
///
/// This routine only examines the state of the instruction at hand.
bool isInstructionTriviallyDead(SILInstruction *inst);

bool canTriviallyDeleteOSSAEndScopeInst(SILInstruction *inst);

/// Return true if this is a release instruction that's not going to
/// free the object.
bool isIntermediateRelease(SILInstruction *inst, EpilogueARCFunctionInfo *erfi);

/// Recursively collect all the uses and transitive uses of the
/// instruction.
void collectUsesOfValue(SILValue V,
                        llvm::SmallPtrSetImpl<SILInstruction *> &Insts);

/// Recursively erase all of the uses of the value (but not the
/// value itself)
void eraseUsesOfValue(SILValue value);

/// Return true if \p type is a value type (struct/enum) that requires
/// deinitialization beyond destruction of its members.
bool hasValueDeinit(SILType type);

/// Return true if \p value has a value type (struct/enum) that requires
/// deinitialization beyond destruction of its members.
inline bool hasValueDeinit(SILValue value) {
  return hasValueDeinit(value->getType());
}

/// Gets the concrete value which is stored in an existential box.
/// Returns %value in following pattern:
///
///    %existentialBox = alloc_existential_box $Error, $ConcreteError
///    %a = project_existential_box $ConcreteError in %existentialBox : $Error
///    store %value to %a : $*ConcreteError
///
/// Returns an invalid SILValue in case there are multiple stores or any unknown
/// users of \p existentialBox.
/// The \p ignoreUser is ignored in the user list of \p existentialBox.
SILValue
getConcreteValueOfExistentialBox(AllocExistentialBoxInst *existentialBox,
                                 SILInstruction *ignoreUser);

/// Gets the concrete value which is stored in an existential box, which itself
/// is stored in \p addr.
/// Returns %value in following pattern:
///
///    %b = alloc_existential_box $Error, $ConcreteError
///    %a = project_existential_box $ConcreteError in %b : $Error
///    store %value to %a : $*ConcreteError
///    %addr = alloc_stack $Error
///    store %b to %addr : $*Error
///
/// Returns an invalid SILValue in case there are multiple stores or any unknown
/// users of \p addr or the existential box.
/// The \p ignoreUser is ignored in the user list of \p addr.
SILValue getConcreteValueOfExistentialBoxAddr(SILValue addr,
                                              SILInstruction *ignoreUser);

/// Cast a value into the expected, ABI compatible type if necessary.
/// This may happen e.g. when:
/// - a type of the return value is a subclass of the expected return type.
/// - actual return type and expected return type differ in optionality.
/// - both types are tuple-types and some of the elements need to be casted.
///
/// \p usePoints is required when \p value has guaranteed ownership. It must be
/// the last users of the returned, casted value. A usePoint cannot be a
/// BranchInst (a phi is never the last guaranteed user). \p builder's current
/// insertion point must dominate all \p usePoints.
std::pair<SILValue, bool /* changedCFG */>
castValueToABICompatibleType(SILBuilder *builder, SILPassManager *pm,
                             SILLocation Loc,
                             SILValue value, SILType srcTy, SILType destTy,
                             ArrayRef<SILInstruction *> usePoints);

/// Returns true if the layout of a generic nominal type is dependent on its generic parameters.
/// This is usually the case. Some examples, where they layout is _not_ dependent:
/// ```
///    struct S<T> {
///      var x: Int // no members which depend on T
///    }
///
///    struct S<T> {
///      var c: SomeClass<T> // a class reference does not depend on the layout of the class
///    }
/// ```
bool layoutIsTypeDependent(NominalTypeDecl *decl);

/// Peek through trivial Enum initialization, typically for pointless
/// Optionals.
///
/// The returned InitEnumDataAddr dominates the given
/// UncheckedTakeEnumDataAddrInst.
InitEnumDataAddrInst *
findInitAddressForTrivialEnum(UncheckedTakeEnumDataAddrInst *utedai);

/// Returns a project_box if it is the next instruction after \p ABI and
/// and has \p ABI as operand. Otherwise it creates a new project_box right
/// after \p ABI and returns it.
ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *abi, unsigned index);

/// Return true if any call inside the given function may bind dynamic
/// 'Self' to a generic argument of the callee.
bool mayBindDynamicSelf(SILFunction *f);

/// Check whether the \p addr is an address of a tail-allocated array element.
bool isAddressOfArrayElement(SILValue addr);

/// Move an ApplyInst's FuncRef so that it dominates the call site.
void placeFuncRef(ApplyInst *ai, DominanceInfo *dt);

/// Add arguments, \p vals, to the branch-edge that is pointing into
/// block \p Dest. Return a new instruction and do not erase the old
/// instruction.
TermInst *addArgumentsToBranch(ArrayRef<SILValue> vals, SILBasicBlock *dest,
                               TermInst *branch);

/// Get the linkage to be used for specializations of a function with
/// the given linkage.
SILLinkage getSpecializedLinkage(SILFunction *f, SILLinkage linkage);

/// Tries to perform jump-threading on all checked_cast_br instruction in
/// function \p Fn.
bool tryCheckedCastBrJumpThreading(
    SILFunction *fn, SILPassManager *pm, DominanceInfo *dt, DeadEndBlocks *deBlocks,
    SmallVectorImpl<SILBasicBlock *> &blocksForWorklist,
    bool EnableOSSARewriteTerminator);

/// Get all consumed arguments of a partial_apply.
///
/// These are basically all arguments, except inout arguments and arguments
/// of trivial type.
/// If \p includeTrivialAddrArgs is true, also trivial address-type arguments
/// are included.
void getConsumedPartialApplyArgs(PartialApplyInst *pai,
                                 SmallVectorImpl<Operand *> &argOperands,
                                 bool includeTrivialAddrArgs);

/// Emit destroy operation for \p operand, and call appropriate functions from
/// \p callbacks for newly created instructions and deleted instructions.
void emitDestroyOperation(SILBuilder &builder, SILLocation loc,
                          SILValue operand, InstModCallbacks callbacks);

/// Collect all (transitive) users of \p inst which just copy or destroy \p
/// inst.
///
/// In other words: all users which do not prevent \p inst from being considered
/// as "dead".
/// Returns true, if there are no other users beside those collected in \p
/// destroys, i.e. if \p inst can be considered as "dead".
bool collectDestroys(SingleValueInstruction *inst,
                     SmallVectorImpl<Operand *> &destroys);

/// If Closure is a partial_apply or thin_to_thick_function with only local
/// ref count users and a set of post-dominating releases:
///
/// 1. Remove all ref count operations and the closure.
/// 2. At each one of the last release locations insert releases for the
///    captured args if we have a partial_apply (except \p needKeepArgsAlive is
///    false).
///
/// In the future this should be extended to be less conservative with users.
bool tryDeleteDeadClosure(SingleValueInstruction *closure,
                          InstModCallbacks callbacks = InstModCallbacks(),
                          bool needKeepArgsAlive = true);

/// Given a SILValue argument to a partial apply \p Arg and the associated
/// parameter info for that argument, perform the necessary cleanups to Arg when
/// one is attempting to delete the partial apply.
void releasePartialApplyCapturedArg(
    SILBuilder &builder, SILLocation loc, SILValue arg,
    SILParameterInfo paramInfo,
    InstModCallbacks callbacks = InstModCallbacks());

/// Insert destroys of captured arguments of partial_apply [stack]. \p builder
/// indicates a position at which the closure's lifetime ends.
///
/// The \p getValueToDestroy callback allows the caller to handle some captured
/// arguments specially. For example, ClosureLifetimeFixup generates borrow
/// scopes for captured arguments; each getValueToDestroy callback then inserts
/// the corresponding end_borrow and returns the owned operand of the borrow,
/// which will then be destroyed as usual.
void insertDestroyOfCapturedArguments(
    PartialApplyInst *pai, SILBuilder &builder,
    llvm::function_ref<SILValue(SILValue)> getValueToDestroy =
        [](SILValue arg) -> SILValue { return arg; },
    SILLocation loc = RegularLocation::getAutoGeneratedLocation());

void insertDeallocOfCapturedArguments(PartialApplyInst *pai,
    DominanceInfo *domInfo,
    llvm::function_ref<SILValue(SILValue)> getAddressToDealloc =
      [](SILValue arg) -> SILValue { return arg; });

/// This iterator 'looks through' one level of builtin expect users exposing all
/// users of the looked through builtin expect instruction i.e it presents a
/// view that shows all users as if there were no builtin expect instructions
/// interposed.
class IgnoreExpectUseIterator {
public:
  using iterator_category = std::forward_iterator_tag;
  using value_type = Operand*;
  using difference_type = std::ptrdiff_t;
  using pointer = value_type*;
  using reference = value_type&;    

private:
  ValueBaseUseIterator origUseChain;
  ValueBaseUseIterator currentIter;

  static BuiltinInst *isExpect(Operand *use) {
    if (auto *bi = dyn_cast<BuiltinInst>(use->getUser()))
      if (bi->getIntrinsicInfo().ID == llvm::Intrinsic::expect)
        return bi;
    return nullptr;
  }

  // Advance through expect users to their users until we encounter a user that
  // is not an expect.
  void advanceThroughExpects() {
    while (currentIter == origUseChain
           && currentIter != ValueBaseUseIterator(nullptr)) {
      auto *Expect = isExpect(*currentIter);
      if (!Expect)
        return;
      currentIter = Expect->use_begin();
      // Expect with no users advance to next item in original use chain.
      if (currentIter == Expect->use_end())
        currentIter = ++origUseChain;
    }
  }

public:
  IgnoreExpectUseIterator(ValueBase *value)
      : origUseChain(value->use_begin()), currentIter(value->use_begin()) {
    advanceThroughExpects();
  }

  IgnoreExpectUseIterator() = default;

  Operand *operator*() const { return *currentIter; }
  Operand *operator->() const { return *currentIter; }
  SILInstruction *getUser() const { return currentIter->getUser(); }

  IgnoreExpectUseIterator &operator++() {
    assert(**this && "increment past end()!");
    if (origUseChain == currentIter) {
      // Use chain of the original value.
      ++origUseChain;
      ++currentIter;
      // Ignore expects.
      advanceThroughExpects();
    } else {
      // Use chain of an expect.
      ++currentIter;
      if (currentIter == ValueBaseUseIterator(nullptr)) {
        // At the end of the use chain of an expect.
        currentIter = ++origUseChain;
        advanceThroughExpects();
      }
    }
    return *this;
  }

  IgnoreExpectUseIterator operator++(int unused) {
    IgnoreExpectUseIterator copy = *this;
    ++*this;
    return copy;
  }
  friend bool operator==(IgnoreExpectUseIterator lhs,
                         IgnoreExpectUseIterator rhs) {
    return lhs.currentIter == rhs.currentIter;
  }
  friend bool operator!=(IgnoreExpectUseIterator lhs,
                         IgnoreExpectUseIterator rhs) {
    return !(lhs == rhs);
  }
};

inline iterator_range<IgnoreExpectUseIterator>
ignore_expect_uses(ValueBase *value) {
  return make_range(IgnoreExpectUseIterator(value), IgnoreExpectUseIterator());
}

/// Run simplifyInstruction() on all of the instruction I's users if they only
/// have one result (since simplifyInstruction assumes that). Replace all uses
/// of the user with its simplification of we succeed. Returns true if we
/// succeed and false otherwise.
///
/// An example of how this is useful is in cases where one is splitting up an
/// aggregate and reforming it, the reformed aggregate may have extract
/// operations from it. These can be simplified and removed.
bool simplifyUsers(SingleValueInstruction *inst);

/// Check if the value of value is computed by means of a simple initialization.
/// Store the actual SILValue into \p Val and the reversed list of instructions
/// initializing it in \p Insns.
/// The check is performed by recursively walking the computation of the
/// SIL value being analyzed.
bool analyzeStaticInitializer(SILValue value,
                              SmallVectorImpl<SILInstruction *> &insns);

/// Returns true if the below operation will succeed.
bool canReplaceLoadSequence(SILInstruction *inst);

/// Replace load sequence which may contain
/// a chain of struct_element_addr followed by a load.
/// The sequence is traversed inside out, i.e.
/// starting with the innermost struct_element_addr
void replaceLoadSequence(SILInstruction *inst, SILValue value);

// Attempt to get the instance for , whose static type is the same as
// its exact dynamic type, returning a null SILValue() if we cannot find it.
// The information that a static type is the same as the exact dynamic,
// can be derived e.g.:
// - from a constructor or
// - from a successful outcome of a checked_cast_br [exact] instruction.
SILValue getInstanceWithExactDynamicType(SILValue instance,
                                         ClassHierarchyAnalysis *cha);

/// Try to determine the exact dynamic type of an object.
/// returns the exact dynamic type of the object, or an empty type if the exact
/// type could not be determined.
SILType getExactDynamicType(SILValue instance, ClassHierarchyAnalysis *cha,
                            bool forUnderlyingObject = false);

/// Try to statically determine the exact dynamic type of the underlying object.
/// returns the exact dynamic type of the underlying object, or an empty SILType
/// if the exact type could not be determined.
SILType getExactDynamicTypeOfUnderlyingObject(SILValue instance,
                                              ClassHierarchyAnalysis *cha);

// Move only data structure that is the result of findLocalApplySite.
///
/// NOTE: Generally it is not suggested to have move only types that contain
/// small vectors. Since our small vectors contain one element or a std::vector
/// like data structure , this is ok since we will either just copy the single
/// element when we do the move or perform a move of the vector type.
struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult {
  /// Contains the list of local non fully applied partial apply sites that we
  /// found.
  SmallVector<ApplySite, 1> partialApplySites;

  /// Contains the list of full apply sites that we found.
  SmallVector<FullApplySite, 1> fullApplySites;

  /// Set to true if the function_ref escapes into a use that our analysis does
  /// not understand. Set to false if we found a use that had an actual
  /// escape. Set to None if we did not find any call sites, but also didn't
  /// find any "escaping uses" as well.
  ///
  /// The none case is so that we can distinguish in between saying that a value
  /// did escape and saying that we did not find any conservative information.
  bool escapes;

  FindLocalApplySitesResult() = default;
  FindLocalApplySitesResult(const FindLocalApplySitesResult &) = delete;
  FindLocalApplySitesResult &
  operator=(const FindLocalApplySitesResult &) = delete;
  FindLocalApplySitesResult(FindLocalApplySitesResult &&) = default;
  FindLocalApplySitesResult &operator=(FindLocalApplySitesResult &&) = default;
  ~FindLocalApplySitesResult() = default;

  /// Treat this function ref as escaping only if we found an actual user we
  /// didn't understand. Do not treat it as escaping if we did not find any
  /// users at all.
  bool isEscaping() const { return escapes; }
};

/// Returns .some(FindLocalApplySitesResult) if we found any interesting
/// information for the given function_ref. Otherwise, returns None.
///
/// We consider "interesting information" to mean inclusively that:
///
/// 1. We discovered that the function_ref never escapes.
/// 2. We were able to find either a partial apply or a full apply site.
std::optional<FindLocalApplySitesResult>
findLocalApplySites(FunctionRefBaseInst *fri);

/// Gets the base implementation of a method.
AbstractFunctionDecl *getBaseMethod(AbstractFunctionDecl *FD);

bool tryOptimizeApplyOfPartialApply(
    PartialApplyInst *pai, SILBuilderContext &builderCtxt,
    InstModCallbacks callbacks = InstModCallbacks());

/// Clone this full apply site, replacing the callee with \p newCallee while
/// doing so.
///
/// The current full apply site is used as an insertion point, so the caller
/// must clean up this full apply site.
FullApplySite cloneFullApplySiteReplacingCallee(FullApplySite applySite,
                                                SILValue newCallee,
                                                SILBuilderContext &builderCtx);

/// Replace all uses of \p oldValue with \p newValue, notifying the callbacks
/// of new uses and when end-of-scope instructions are deleted.
SILBasicBlock::iterator replaceAllUses(SILValue oldValue, SILValue newValue,
                                       SILBasicBlock::iterator nextii,
                                       InstModCallbacks &callbacks);

/// This is a low level routine that makes all uses of \p svi uses of \p
/// newValue (ignoring end scope markers) and then deletes \p svi and all end
/// scope markers. Then returns the next inst to process.
SILBasicBlock::iterator replaceAllUsesAndErase(SingleValueInstruction *svi,
                                               SILValue newValue,
                                               InstModCallbacks &callbacks);

/// Replace all uses of \p oldValue with \p newValue, delete the instruction
/// that defines \p oldValue, and notify the callbacks of new uses and when
/// the defining instruction and its end-of-scope instructions are deleted.
///
/// Precondition: \p oldValue must be a SingleValueInstruction or a terminator
/// result. \p oldValue must be the only result with remaining uses. For
/// terminators with multiple results, remove all other results for, e.g. via
/// replaceAllUsesWithUndef().
///
/// If \p oldValue is a terminator result, a new branch instruction is inserted
/// in place of the old terminator and all basic block successors become
/// unreachable except for the successor containing the replaced result.
SILBasicBlock::iterator replaceAllUsesAndErase(SILValue oldValue,
                                               SILValue newValue,
                                               InstModCallbacks &callbacks);

/// This API is equivalent to performing \p use->set(\p newValue) except that:
///
/// 1. If the user of \p use is an end scope, this API no-opts. This API is only
///    used in contexts where we are rewriting uses and are not interesting in
///    end scope instructions since we are moving uses from one scope to another
///    scope.
///
/// 2. If the user of \p use is not an end scope, but is a lifetime ending use
///    of \p use->get(), we insert a destroy_value|end_borrow as appropriate on
///    \p use->get() to ensure \p use->get()'s lifetime is still ended. We
///    assume that if \p use->getUser() is lifetime ending, that our caller has
///    ensured that we can end \p newValue's lifetime.
SILBasicBlock::iterator replaceSingleUse(Operand *use, SILValue newValue,
                                         InstModCallbacks &callbacks);

/// Creates a copy of \p value and inserts additional control equivalent copy
/// and destroy at leaking blocks to adjust ownership and make available for use
/// at \p inBlock.
SILValue
makeCopiedValueAvailable(SILValue value, SILBasicBlock *inBlock);

/// Given an existing @owned value \p value, this utility
/// inserts control equivalent copy and destroy at leaking blocks to adjust
/// ownership and make \p value available for use at \p inBlock.
SILValue makeValueAvailable(SILValue value, SILBasicBlock *inBlock);

/// Given an ssa value \p value, create destroy_values at leaking blocks
///
/// Warning: This does not properly cleanup an OSSA lifetime with a consuming
/// use blocks inside a loop relative to \p value. The client must create
/// separate copies for any uses within the loop.
void endLifetimeAtLeakingBlocks(SILValue value,
                                ArrayRef<SILBasicBlock *> userBBs,
                                DeadEndBlocks *deadEndBlocks = nullptr);

/// Given a forwarding instruction, eliminate it if all of its users are debug
/// instructions and ownership uses.
bool tryEliminateOnlyOwnershipUsedForwardingInst(
    SingleValueInstruction *forwardingInst, InstModCallbacks &callbacks);

/// Constant-fold the Builtin.canBeClass if the type is known.
IntegerLiteralInst *optimizeBuiltinCanBeObjCClass(BuiltinInst *bi,
                                                  SILBuilder &builder);

/// Performs "predictable" dead allocation optimizations.
///
/// See the PredictableDeadAllocationElimination pass.
bool eliminateDeadAllocations(SILFunction *fn, DominanceInfo *domInfo);

bool specializeClassMethodInst(ClassMethodInst *cm);
bool specializeWitnessMethodInst(WitnessMethodInst *wm);

bool specializeAppliesInFunction(SILFunction &F,
                                 SILTransform *transform,
                                 bool isMandatory);

bool tryOptimizeKeypath(ApplyInst *AI, SILBuilder Builder);
bool tryOptimizeKeypathApplication(ApplyInst *AI, SILFunction *callee, SILBuilder Builder);
bool tryOptimizeKeypathOffsetOf(ApplyInst *AI, FuncDecl *calleeFn,
                                KeyPathInst *kp, SILBuilder Builder);
bool tryOptimizeKeypathKVCString(ApplyInst *AI, FuncDecl *calleeFn,
                                KeyPathInst *kp, SILBuilder Builder);

/// Instantiate the specified type by recursively tupling and structing the
/// unique instances of the empty types and undef "instances" of the non-empty
/// types aggregated together at each level.
SILValue createEmptyAndUndefValue(SILType ty, SILInstruction *insertionPoint,
                                  SILBuilderContext &ctx, bool noUndef = false);

/// Check if a struct or its fields can have unreferenceable storage.
bool findUnreferenceableStorage(StructDecl *decl, SILType structType,
                                SILFunction *func);

SILValue getInitOfTemporaryAllocStack(AllocStackInst *asi);

bool isDestructorSideEffectFree(SILInstruction *mayRelease,
                                DestructorAnalysis *DA);

} // end namespace swift

#endif // SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H
