//===--- 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 #include namespace swift { class DominanceInfo; template class NullablePtr; /// Transform a Use Range (Operand*) into a User Range (SILInstruction*) using UserTransform = std::function; using ValueBaseUserRange = TransformRange, UserTransform>; inline ValueBaseUserRange makeUserRange( iterator_range R) { auto toUser = [](Operand *O) { return O->getUser(); }; return makeTransformRange(makeIteratorRange(R.begin(), R.end()), UserTransform(toUser)); } using DeadInstructionSet = llvm::SmallSetVector; /// Create a retain of \p Ptr before the \p InsertPt. NullablePtr createIncrementBefore(SILValue Ptr, SILInstruction *InsertPt); /// Create a release of \p Ptr before the \p InsertPt. NullablePtr 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 I, bool Force = false, llvm::function_ref 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 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 &Insts); /// Recursively erase all of the uses of the instruction (but not the /// instruction itself) void eraseUsesOfInstruction( SILInstruction *Inst, llvm::function_ref 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 &BlocksForWorklist); /// A structure containing callbacks that are called when an instruction is /// removed or added. struct InstModCallbacks { using CallbackTy = std::function; 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 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 Frontier; /// Constructor for the value \p Def with a specific set of users of Def's /// users. ValueLifetimeAnalysis(SILInstruction *Def, ArrayRef 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 LiveBlocks; /// The set of instructions where the value is used, or the users-list /// provided with the constructor. llvm::SmallPtrSet 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 { using SuperTy = SILCloner; friend class SILCloner; 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, 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 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(Value)) { if (!isBlockCloned(BBArg->getParent())) return Value; } else { assert(isa(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 value_type; typedef std::function CallbackType; typedef std::function FilterType; private: FilterType Filter; // Pairs of collected instructions; (new, old) llvm::SmallVector 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 &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 { ValueBaseUseIterator OrigUseChain; ValueBaseUseIterator CurrentIter; static BuiltinInst *isExpect(Operand *Use) { if (auto *BI = dyn_cast(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 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 &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 { friend class SILInstructionVisitor; friend class SILCloner; /// The number of not yet cloned operands for each instruction. llvm::DenseMap NumOpsToClone; /// List of instructions for which all operands are already cloned (or which /// don't have any operands). llvm::SmallVector ReadyToClone; public: StaticInitCloner(SILGlobalVariable *GVar) : SILCloner(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 partialApplySites; /// Contains the list of full apply sites that we found. SmallVector 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 findLocalApplySites(FunctionRefBaseInst *FRI); } // end namespace swift #endif