//===--- Local.h - Local SIL transformations. -------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://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/Basic/ArrayRefView.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; /// 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)); } /// \brief For each of the given instructions, if they are dead delete them /// along with their dead operands. /// /// \param I The instruction 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, std::function C = [](SILInstruction *){}); /// \brief 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, std::function C = [](SILInstruction *){}); /// \brief 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); /// \brief Return true if this is a release instruction and the released value /// is a part of a guaranteed parameter, false otherwise. bool isGuaranteedParamRelease(SILInstruction *I); /// \brief Recursively erase all of the uses of the instruction (but not the /// instruction itself) and delete instructions that will become trivially /// dead when this instruction is removed. void eraseUsesOfInstruction( SILInstruction *Inst, std::function C = [](SILInstruction *){}); /// \brief Recursively erase all of the uses of the value (but not the /// value itself) void eraseUsesOfValue(SILValue V); FullApplySite findApplyFromDevirtualizedResult(SILInstruction *I); /// Check that this is a partial apply of a reabstraction thunk and return the /// argument of the partial apply if it is. SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); /// 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. /// /// If CheckOnly flag is set, then this function only checks if the /// required casting is possible. If it is not possible, then None /// is returned. /// If CheckOnly is not set, then a casting code is generated and the final /// casted value is returned. Optional castValueToABICompatibleType(SILBuilder *B, SILLocation Loc, SILValue Value, SILType SrcTy, SILType DestTy, bool CheckOnly = false); /// Check if the optimizer can cast a value into the expected, /// ABI compatible type if necessary. bool canCastValueToABICompatibleType(SILModule &M, SILType SrcTy, SILType DestTy); /// 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); /// Replace an apply with an instruction that produces the same value, /// then delete the apply and the instructions that produce its callee /// if possible. void replaceDeadApply(ApplySite Old, ValueBase *New); /// \brief Return true if the substitution map contains a /// substitution that is an unbound generic type. bool hasUnboundGenericTypes(TypeSubstitutionMap &SubsMap); /// Return true if the substitution list contains a substitution /// that is an unbound generic. bool hasUnboundGenericTypes(ArrayRef Subs); /// \brief Return true if the substitution map contains a /// substitution that refers to the dynamic Self type. bool hasDynamicSelfTypes(TypeSubstitutionMap &SubsMap); /// \brief Return true if the substitution list contains a /// substitution that refers to the dynamic Self type. bool hasDynamicSelfTypes(ArrayRef Subs); /// \brief Return true if any call inside the given function may bind dynamic /// 'Self' to a generic argument of the callee. bool computeMayBindDynamicSelf(SILFunction *F); /// \brief Move an ApplyInst's FuncRef so that it dominates the call site. void placeFuncRef(ApplyInst *AI, DominanceInfo *DT); /// \brief 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); /// \brief 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. SILInstruction *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); void recalcDomTreeForCCBOpt(DominanceInfo *DT, SILFunction &F); /// Checks if a symbol with a given linkage can be referenced from fragile /// functions. bool isValidLinkageForFragileRef(SILLinkage linkage); /// 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(SILInstruction *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()); /// 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(SILValue Def, ArrayRef UserList) : DefValue(Def), UserSet(UserList.begin(), UserList.end()) { propagateLiveness(); } /// Constructor for the value \p Def considering all the value's uses. ValueLifetimeAnalysis(SILValue Def) : DefValue(Def) { for (Operand *Op : Def->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, /// Ignore exit edges from the lifetime region at all. IgnoreExitEdges }; /// 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. bool computeFrontier(Frontier &Fr, Mode mode); /// 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); private: /// The value. SILValue 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); }; /// Base class for BB cloners. class BaseThreadingCloner : public SILClonerWithScopes { friend class SILVisitor; friend class SILCloner; protected: SILBasicBlock *FromBB, *DestBB; public: // A map of old to new available values. SmallVector, 16> AvailVals; /// If WithinFunction is true, the debug scopes of the cloned /// instructions will not be updated. BaseThreadingCloner(SILFunction &To, bool WithinFunction) : SILClonerWithScopes(To, WithinFunction), FromBB(nullptr), DestBB(nullptr) {} BaseThreadingCloner(SILFunction &To, SILBasicBlock *From, SILBasicBlock *Dest) : SILClonerWithScopes(To, From->getParent() == &To), FromBB(From), DestBB(Dest) {} void process(SILInstruction *I) { visit(I); } SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; } SILValue remapValue(SILValue Value) { // If this is a use of an instruction in another block, then just use it. if (auto SI = dyn_cast(Value)) { if (SI->getParent() != FromBB) return Value; } else if (auto BBArg = dyn_cast(Value)) { if (BBArg->getParent() != FromBB) return Value; } else { assert(isa(Value) && "Unexpected Value kind"); return Value; } return SILCloner::remapValue(Value); } void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { DestBB->push_back(Cloned); SILCloner::postProcess(Orig, Cloned); // A terminator defines no values. Keeping terminators in the AvailVals list // is problematic because terminators get replaced during SSA update. if (!isa(Orig)) AvailVals.push_back(std::make_pair(Orig, SILValue(Cloned))); } }; /// Clone a basic block to edge \p BI. class EdgeThreadingCloner : public BaseThreadingCloner { public: EdgeThreadingCloner(BranchInst *BI) : BaseThreadingCloner(*BI->getFunction(), BI->getDestBB(), nullptr) { DestBB = createEdgeBlockAndRedirectBranch(BI); } SILBasicBlock *createEdgeBlockAndRedirectBranch(BranchInst *BI) { auto *Fn = BI->getFunction(); auto *SrcBB = BI->getParent(); auto *DestBB = BI->getDestBB(); auto *EdgeBB = new (Fn->getModule()) SILBasicBlock(Fn, SrcBB); // Create block arguments. unsigned ArgIdx = 0; for (auto Arg : BI->getArgs()) { assert(Arg->getType() == DestBB->getBBArg(ArgIdx)->getType() && "Types must match"); auto *BlockArg = EdgeBB->createBBArg(Arg->getType()); ValueMap[DestBB->getBBArg(ArgIdx)] = SILValue(BlockArg); AvailVals.push_back(std::make_pair(DestBB->getBBArg(ArgIdx), BlockArg)); ++ArgIdx; } // Redirect the branch. SILBuilderWithScope(BI).createBranch(BI->getLoc(), EdgeBB, BI->getArgs()); BI->eraseFromParent(); return EdgeBB; } SILBasicBlock *getEdgeBB() { // DestBB really is the edge basic block we created to clone instructions // to. return DestBB; } }; /// Helper class for cloning of basic blocks. class BasicBlockCloner : public BaseThreadingCloner { public: BasicBlockCloner(SILBasicBlock *From, SILBasicBlock *To = nullptr, bool WithinFunction = true) : BaseThreadingCloner(To ? *To->getParent() : *From->getParent(), WithinFunction) { FromBB = From; if (To == nullptr) { // Create a new BB that is to be used as a target // for cloning. To = From->getParent()->createBasicBlock(); for (auto *Arg : FromBB->getBBArgs()) { To->createBBArg(Arg->getType(), Arg->getDecl()); } } DestBB = To; // Populate the value map so that uses of the BBArgs in the SrcBB are // replaced with the BBArgs of the DestBB. for (unsigned i = 0, e = FromBB->bbarg_size(); i != e; ++i) { ValueMap[FromBB->getBBArg(i)] = DestBB->getBBArg(i); AvailVals.push_back( std::make_pair(FromBB->getBBArg(i), DestBB->getBBArg(i))); } } // Clone all instructions of the FromBB into DestBB void clone() { for (auto &I : *FromBB) process(&I); } SILBasicBlock *getDestBB() { return DestBB; } }; /// Helper function to perform SSA updates in case of jump threading. Set /// 'NeedToSplitCriticalEdges' to false if all critical edges are split, /// otherwise this call will try to split all critical edges. void updateSSAAfterCloning(BaseThreadingCloner &Cloner, SILBasicBlock *SrcBB, SILBasicBlock *DestBB, bool NeedToSplitCriticalEdges = true); /// \brief This is a helper class used to optimize casts. class CastOptimizer { // Callback to be called when uses of an instruction should be replaced. std::function ReplaceInstUsesAction; // Callback to call when an instruction needs to be erased. std::function EraseInstAction; // Callback to call after an optimization was performed based on the fact // that a cast will succeed. std::function WillSucceedAction; // Callback to call after an optimization was performed based on the fact // that a cast will fail. std::function WillFailAction; /// Optimize a cast from a bridged ObjC type into /// a corresponding Swift type implementing _ObjectiveCBridgeable. SILInstruction * optimizeBridgedObjCToSwiftCast(SILInstruction *Inst, bool isConditional, SILValue Src, SILValue Dest, CanType Source, CanType Target, Type BridgedSourceTy, Type BridgedTargetTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB); /// Optimize a cast from a Swift type implementing _ObjectiveCBridgeable /// into a bridged ObjC type. SILInstruction * optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, bool isConditional, SILValue Src, SILValue Dest, CanType Source, CanType Target, Type BridgedSourceTy, Type BridgedTargetTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB); public: CastOptimizer(std::function ReplaceInstUsesAction, std::function EraseAction = [](SILInstruction*){}, std::function WillSucceedAction = [](){}, std::function WillFailAction = [](){}) : ReplaceInstUsesAction(ReplaceInstUsesAction), EraseInstAction(EraseAction), WillSucceedAction(WillSucceedAction), WillFailAction(WillFailAction) {} /// Simplify checked_cast_br. It may change the control flow. SILInstruction * simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst); /// Simplify checked_cast_addr_br. It may change the control flow. SILInstruction * simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst); /// Optimize checked_cast_br. This cannot change the control flow. SILInstruction * optimizeCheckedCastBranchInst(CheckedCastBranchInst *Inst); /// Optimize checked_cast_addr_br. This cannot change the control flow. SILInstruction * optimizeCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst); /// Optimize unconditional_checked_cast. This cannot change the control flow. ValueBase * optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst); /// Optimize unconditional_checked_cast_addr. This cannot change the control /// flow. SILInstruction * optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst); /// Check if it is a bridged cast and optimize it. /// May change the control flow. SILInstruction * optimizeBridgedCasts(SILInstruction *Inst, bool isConditional, SILValue Src, SILValue Dest, CanType Source, CanType Target, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB); }; // 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 bool isExpect(Operand *Use) { if (auto *BI = dyn_cast(Use->getUser())) if (BI->getIntrinsicInfo().ID == llvm::Intrinsic::expect) return true; return false; } // 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) && isExpect(*CurrentIter)) { auto *Expect = CurrentIter->getUser(); 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(SILInstruction *I); /// Check if a given type is a simple type, i.e. a builtin /// integer or floating point type or a struct/tuple whose members /// are of simple types. bool isSimpleType(SILType SILTy, SILModule& Module); /// 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); /// 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, SILInstruction *Value, SILBuilder &B); /// 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); /// Hoist the address projection rooted in \p Op to \p InsertBefore. /// Requires the projected value to dominate the insertion point. /// /// Will look through single basic block predecessor arguments. void hoistAddressProjections(Operand &Op, SILInstruction *InsertBefore, DominanceInfo *DomTree); } // end namespace swift #endif