Files
swift-mirror/include/swift/SILOptimizer/Utils/Local.h
Andrew Trick ece096d91e Fix eraseFromParentWithdebugInsts to call a callback.
The recursivelyDeleteTriviallyDeadInstructions utility takes a
callBack to be called for every deleted instruction. However, it
wasn't passing this callBack to eraseFromParentWithdebugInsts. The
callback was used to update an iterator in some cases, so not calling
it resulted in iterator invalidation.

Doing this also cleans up the both APIs:
recursivelyDeleteTriviallyDeadInstructions and eraseFromParentWithdebugInsts.
2019-05-06 17:23:08 -07:00

645 lines
25 KiB
C++

//===--- Local.h - Local SIL transformations. -------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
#define SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILCloner.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Allocator.h"
#include <functional>
#include <utility>
namespace swift {
class DominanceInfo;
template <class T> class NullablePtr;
/// Transform a Use Range (Operand*) into a User Range (SILInstruction*)
using UserTransform = std::function<SILInstruction *(Operand *)>;
using ValueBaseUserRange =
TransformRange<IteratorRange<ValueBase::use_iterator>, UserTransform>;
inline ValueBaseUserRange makeUserRange(
iterator_range<ValueBase::use_iterator> R) {
auto toUser = [](Operand *O) { return O->getUser(); };
return makeTransformRange(makeIteratorRange(R.begin(), R.end()),
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);
/// For each of the given instructions, if they are dead delete them
/// along with their dead operands.
///
/// \param I 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 C a callback called whenever an instruction is deleted.
void
recursivelyDeleteTriviallyDeadInstructions(
ArrayRef<SILInstruction*> I, bool Force = false,
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
/// If the given instruction is dead, delete it along with its dead
/// operands.
///
/// \param I 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 C a callback called whenever an instruction is deleted.
void recursivelyDeleteTriviallyDeadInstructions(
SILInstruction *I, bool Force = false,
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
/// 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 *I);
/// Return true if this is a release instruction that's not going to
/// free the object.
bool isIntermediateRelease(SILInstruction *I, 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 instruction (but not the
/// instruction itself)
void eraseUsesOfInstruction(
SILInstruction *Inst,
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
/// Recursively erase all of the uses of the value (but not the
/// value itself)
void eraseUsesOfValue(SILValue V);
FullApplySite findApplyFromDevirtualizedResult(SILValue value);
/// 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.
SILValue castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
SILValue Value,
SILType SrcTy,
SILType DestTy);
/// 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 an argument, \p val, to the branch-edge that is pointing into
/// block \p Dest. Return a new instruction and do not erase the old
/// instruction.
TermInst *addArgumentToBranch(SILValue Val, SILBasicBlock *Dest,
TermInst *Branch);
/// Handle the mechanical aspects of removing an unreachable block.
void removeDeadBlock(SILBasicBlock *BB);
/// Remove all instructions in the body of \p BB in safe manner by using
/// undef.
void clearBlockBody(SILBasicBlock *BB);
/// Get the linkage to be used for specializations of a function with
/// the given linkage.
SILLinkage getSpecializedLinkage(SILFunction *F, SILLinkage L);
/// Tries to optimize a given apply instruction if it is a concatenation of
/// string literals. Returns a new instruction if optimization was possible.
SingleValueInstruction *tryToConcatenateStrings(ApplyInst *AI, SILBuilder &B);
/// Tries to perform jump-threading on all checked_cast_br instruction in
/// function \p Fn.
bool tryCheckedCastBrJumpThreading(SILFunction *Fn, DominanceInfo *DT,
SmallVectorImpl<SILBasicBlock *> &BlocksForWorklist);
/// A structure containing callbacks that are called when an instruction is
/// removed or added.
struct InstModCallbacks {
using CallbackTy = std::function<void (SILInstruction *)>;
CallbackTy DeleteInst = [](SILInstruction *I) {
I->eraseFromParent();
};
CallbackTy CreatedNewInst = [](SILInstruction *){};
InstModCallbacks(CallbackTy DeleteInst, CallbackTy CreatedNewInst)
: DeleteInst(DeleteInst), CreatedNewInst(CreatedNewInst) {}
InstModCallbacks() = default;
~InstModCallbacks() = default;
InstModCallbacks(const InstModCallbacks &) = default;
InstModCallbacks(InstModCallbacks &&) = default;
};
/// 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. Add each one of the last release locations insert releases for the
/// captured args if we have a partial_apply.
///
/// In the future this should be extended to be less conservative with users.
bool
tryDeleteDeadClosure(SingleValueInstruction *Closure,
InstModCallbacks Callbacks = InstModCallbacks());
/// 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 PInfo,
InstModCallbacks Callbacks = InstModCallbacks());
/// Insert destroys of captured arguments of partial_apply [stack].
void insertDestroyOfCapturedArguments(
PartialApplyInst *PAI, SILBuilder &B,
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
[](SILValue arg) -> bool { return true; });
/// This computes the lifetime of a single SILValue.
///
/// This does not compute a set of jointly postdominating use points. Instead it
/// assumes that the value's existing uses already jointly postdominate the
/// definition. This makes sense for values that are returned +1 from an
/// instruction, like partial_apply, and therefore must be released on all paths
/// via strong_release or apply.
class ValueLifetimeAnalysis {
public:
/// The lifetime frontier for the value. It is the list of instructions
/// following the last uses of the value. All the frontier instructions
/// end the value's lifetime.
typedef llvm::SmallVector<SILInstruction *, 4> Frontier;
/// Constructor for the value \p Def with a specific set of users of Def's
/// users.
ValueLifetimeAnalysis(SILInstruction *Def, ArrayRef<SILInstruction*> UserList) :
DefValue(Def), UserSet(UserList.begin(), UserList.end()) {
propagateLiveness();
}
/// Constructor for the value \p Def considering all the value's uses.
ValueLifetimeAnalysis(SILInstruction *Def) : DefValue(Def) {
for (auto result : Def->getResults()) {
for (Operand *op : result->getUses()) {
UserSet.insert(op->getUser());
}
}
propagateLiveness();
}
enum Mode {
/// Don't split critical edges if the frontier instructions are located on
/// a critical edges. Instead fail.
DontModifyCFG,
/// Split critical edges if the frontier instructions are located on
/// a critical edges.
AllowToModifyCFG,
/// Require that all users must commonly post-dominate the definition. In
/// other words: All paths from the definition to the function exit must
/// contain at least one use. Fail if this is not the case.
UsersMustPostDomDef
};
/// Computes and returns the lifetime frontier for the value in \p Fr.
///
/// Returns true if all instructions in the frontier could be found in
/// non-critical edges.
/// Returns false if some frontier instructions are located on critical edges.
/// In this case, if \p mode is AllowToModifyCFG, those critical edges are
/// split, otherwise nothing is done and the returned \p Fr is not valid.
///
/// If \p deadEndBlocks is provided, all dead-end blocks are ignored. This
/// prevents unreachable-blocks to be included in the frontier.
bool computeFrontier(Frontier &Fr, Mode mode,
DeadEndBlocks *DEBlocks = nullptr);
/// Returns true if the instruction \p Inst is located within the value's
/// lifetime.
/// It is assumed that \p Inst is located after the value's definition.
bool isWithinLifetime(SILInstruction *Inst);
/// Returns true if the value is alive at the begin of block \p BB.
bool isAliveAtBeginOfBlock(SILBasicBlock *BB) {
return LiveBlocks.count(BB) && BB != DefValue->getParent();
}
/// Checks if there is a dealloc_ref inside the value's live range.
bool containsDeallocRef(const Frontier &Frontier);
/// For debug dumping.
void dump() const;
private:
/// The value.
SILInstruction *DefValue;
/// The set of blocks where the value is live.
llvm::SmallSetVector<SILBasicBlock *, 16> LiveBlocks;
/// The set of instructions where the value is used, or the users-list
/// provided with the constructor.
llvm::SmallPtrSet<SILInstruction*, 16> UserSet;
/// Propagates the liveness information up the control flow graph.
void propagateLiveness();
/// Returns the last use of the value in the live block \p BB.
SILInstruction *findLastUserInBlock(SILBasicBlock *BB);
};
/// Clone a single basic block and any required successor edges within the same
/// function.
class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
using SuperTy = SILCloner<BasicBlockCloner>;
friend class SILCloner<BasicBlockCloner>;
protected:
/// The original block to be cloned.
SILBasicBlock *origBB;
public:
/// An ordered list of old to new available value pairs.
///
/// updateSSAAfterCloning() expects this public field to hold values that may
/// be remapped in the cloned block and live out.
SmallVector<std::pair<SILValue, SILValue>, 16> AvailVals;
// Clone blocks starting at `origBB`, within the same function.
BasicBlockCloner(SILBasicBlock *origBB)
: SILCloner(*origBB->getParent()), origBB(origBB) {}
void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) {
SmallVector<SILBasicBlock *, 4> successorBBs;
successorBBs.reserve(origBB->getSuccessors().size());
llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs));
cloneReachableBlocks(origBB, successorBBs, insertAfterBB);
}
/// Clone the given branch instruction's destination block, splitting
/// its successors, and rewrite the branch instruction.
void cloneBranchTarget(BranchInst *BI) {
assert(origBB == BI->getDestBB());
cloneBlock(/*insertAfter*/BI->getParent());
SILBuilderWithScope(BI).createBranch(BI->getLoc(), getNewBB(),
BI->getArgs());
BI->eraseFromParent();
}
/// Get the newly cloned block corresponding to `origBB`.
SILBasicBlock *getNewBB() {
return remapBasicBlock(origBB);
}
/// Call this after processing all instructions to fix the control flow
/// graph. The branch cloner may have left critical edges.
bool splitCriticalEdges(DominanceInfo *DT, SILLoopInfo *LI);
protected:
// MARK: CRTP overrides.
/// Override getMappedValue to allow values defined outside the block to be
/// cloned to be reused in the newly cloned block.
SILValue getMappedValue(SILValue Value) {
if (auto SI = Value->getDefiningInstruction()) {
if (!isBlockCloned(SI->getParent()))
return Value;
} else if (auto BBArg = dyn_cast<SILArgument>(Value)) {
if (!isBlockCloned(BBArg->getParent()))
return Value;
} else {
assert(isa<SILUndef>(Value) && "Unexpected Value kind");
return Value;
}
// `value` is not defined outside the cloned block, so consult the cloner's
// map of cloned values.
return SuperTy::getMappedValue(Value);
}
void mapValue(SILValue origValue, SILValue mappedValue) {
SuperTy::mapValue(origValue, mappedValue);
AvailVals.emplace_back(origValue, mappedValue);
}
};
/// Helper function to perform SSA updates in case of jump threading.
void updateSSAAfterCloning(BasicBlockCloner &Cloner, SILBasicBlock *SrcBB,
SILBasicBlock *DestBB);
// Helper class that provides a callback that can be used in
// inliners/cloners for collecting new call sites.
class CloneCollector {
public:
typedef std::pair<SILInstruction *, SILInstruction *> value_type;
typedef std::function<void(SILInstruction *, SILInstruction *)> CallbackType;
typedef std::function<bool (SILInstruction *)> FilterType;
private:
FilterType Filter;
// Pairs of collected instructions; (new, old)
llvm::SmallVector<value_type, 4> InstructionPairs;
void collect(SILInstruction *Old, SILInstruction *New) {
if (Filter(New))
InstructionPairs.push_back(std::make_pair(New, Old));
}
public:
CloneCollector(FilterType Filter) : Filter(Filter) {}
CallbackType getCallback() {
return std::bind(&CloneCollector::collect, this, std::placeholders::_1,
std::placeholders::_2);
}
llvm::SmallVectorImpl<value_type> &getInstructionPairs() {
return InstructionPairs;
}
};
/// 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 std::iterator<std::forward_iterator_tag, Operand *, ptrdiff_t> {
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 *V)
: OrigUseChain(V->use_begin()), CurrentIter(V->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 *V) {
return make_range(IgnoreExpectUseIterator(V),
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 *I);
/// True if a type can be expanded
/// without a significant increase to code size.
bool shouldExpand(SILModule &Module, SILType Ty);
/// Check if the value of V 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 V,
SmallVectorImpl<SILInstruction *> &Insns);
/// Returns true if the below operation will succeed.
bool canReplaceLoadSequence(SILInstruction *I);
/// 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 *I,
SILValue Value);
/// Do we have enough information to determine all callees that could
/// be reached by calling the function represented by Decl?
bool calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl);
// Attempt to get the instance for S, 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 S,
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 S,
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 S,
ClassHierarchyAnalysis *CHA);
/// Utility class for cloning init values into the static initializer of a
/// SILGlobalVariable.
class StaticInitCloner : public SILCloner<StaticInitCloner> {
friend class SILInstructionVisitor<StaticInitCloner>;
friend class SILCloner<StaticInitCloner>;
/// The number of not yet cloned operands for each instruction.
llvm::DenseMap<SILInstruction *, int> NumOpsToClone;
/// List of instructions for which all operands are already cloned (or which
/// don't have any operands).
llvm::SmallVector<SILInstruction *, 8> ReadyToClone;
public:
StaticInitCloner(SILGlobalVariable *GVar)
: SILCloner<StaticInitCloner>(GVar) { }
/// Add \p InitVal and all its operands (transitively) for cloning.
///
/// Note: all init values must are added, before calling clone().
void add(SILInstruction *InitVal);
/// Clone \p InitVal and all its operands into the initializer of the
/// SILGlobalVariable.
///
/// \return Returns the cloned instruction in the SILGlobalVariable.
SingleValueInstruction *clone(SingleValueInstruction *InitVal);
/// Convenience function to clone a single \p InitVal.
static void appendToInitializer(SILGlobalVariable *GVar,
SingleValueInstruction *InitVal) {
StaticInitCloner Cloner(GVar);
Cloner.add(InitVal);
Cloner.clone(InitVal);
}
protected:
SILLocation remapLocation(SILLocation Loc) {
return ArtificialUnreachableLocation();
}
};
/// 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.
Optional<FindLocalApplySitesResult>
findLocalApplySites(FunctionRefBaseInst *FRI);
} // end namespace swift
#endif