| //===--- 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/ARCAnalysis.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; |
| |
| /// 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>; |
| |
| /// \brief Create a retain of \p Ptr before the \p InsertPt. |
| SILInstruction *createIncrementBefore(SILValue Ptr, SILInstruction *InsertPt); |
| |
| /// \brief Create a release of \p Ptr before the \p InsertPt. |
| SILInstruction *createDecrementBefore(SILValue Ptr, SILInstruction *InsertPt); |
| |
| /// \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<SILInstruction*> I, bool Force = false, |
| std::function<void(SILInstruction *)> 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<void(SILInstruction *)> 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 that's not going to |
| /// free the object. |
| bool isIntermediateRelease(SILInstruction *I, |
| ConsumedArgToEpilogueReleaseMatcher &ERM); |
| |
| /// \brief Recursively collect all the uses and transitive uses of the |
| /// instruction. |
| void |
| collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl<SILInstruction *> &Insts); |
| |
| /// \brief Recursively erase all of the uses of the instruction (but not the |
| /// instruction itself) |
| void eraseUsesOfInstruction( |
| SILInstruction *Inst, |
| std::function<void(SILInstruction *)> 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<SILValue> 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<Substitution> 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<Substitution> Subs); |
| |
| /// \brief Return true if any call inside the given function may bind dynamic |
| /// 'Self' to a generic argument of the callee. |
| bool mayBindDynamicSelf(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<SILBasicBlock *> &BlocksForWorklist); |
| |
| void recalcDomTreeForCCBOpt(DominanceInfo *DT, SILFunction &F); |
| |
| /// 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(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<SILInstruction *, 4> Frontier; |
| |
| /// Constructor for the value \p Def with a specific set of users of Def's |
| /// users. |
| ValueLifetimeAnalysis(SILValue 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(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); |
| |
| /// For debug dumping. |
| void dump() const; |
| |
| private: |
| |
| /// The value. |
| SILValue 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); |
| }; |
| |
| /// Base class for BB cloners. |
| class BaseThreadingCloner : public SILClonerWithScopes<BaseThreadingCloner> { |
| friend class SILVisitor<BaseThreadingCloner>; |
| friend class SILCloner<BaseThreadingCloner>; |
| |
| protected: |
| SILBasicBlock *FromBB, *DestBB; |
| |
| public: |
| // A map of old to new available values. |
| SmallVector<std::pair<ValueBase *, SILValue>, 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<SILInstruction>(Value)) { |
| if (SI->getParent() != FromBB) |
| return Value; |
| } else if (auto BBArg = dyn_cast<SILArgument>(Value)) { |
| if (BBArg->getParent() != FromBB) |
| return Value; |
| } else { |
| assert(isa<SILUndef>(Value) && "Unexpected Value kind"); |
| return Value; |
| } |
| |
| return SILCloner<BaseThreadingCloner>::remapValue(Value); |
| } |
| |
| void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { |
| DestBB->push_back(Cloned); |
| SILCloner<BaseThreadingCloner>::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<TermInst>(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<void (SILInstruction *I, ValueBase *V)> ReplaceInstUsesAction; |
| |
| // Callback to call when an instruction needs to be erased. |
| std::function<void (SILInstruction *)> EraseInstAction; |
| |
| // Callback to call after an optimization was performed based on the fact |
| // that a cast will succeed. |
| std::function<void ()> WillSucceedAction; |
| |
| // Callback to call after an optimization was performed based on the fact |
| // that a cast will fail. |
| std::function<void ()> 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, |
| CastConsumptionKind ConsumptionKind, |
| bool isConditional, |
| SILValue Src, |
| SILValue Dest, |
| CanType Source, |
| CanType Target, |
| Type BridgedSourceTy, |
| Type BridgedTargetTy, |
| SILBasicBlock *SuccessBB, |
| SILBasicBlock *FailureBB); |
| |
| void deleteInstructionsAfterUnreachable(SILInstruction *UnreachableInst, |
| SILInstruction *TrapInst); |
| |
| public: |
| CastOptimizer(std::function<void (SILInstruction *I, ValueBase *V)> ReplaceInstUsesAction, |
| std::function<void (SILInstruction *)> EraseAction, |
| std::function<void ()> WillSucceedAction, |
| std::function<void ()> WillFailAction = [](){}) |
| : ReplaceInstUsesAction(ReplaceInstUsesAction), |
| EraseInstAction(EraseAction), |
| WillSucceedAction(WillSucceedAction), |
| WillFailAction(WillFailAction) {} |
| |
| // This constructor is used in |
| // 'SILOptimizer/Mandatory/ConstantPropagation.cpp'. MSVC2015 compiler |
| // couldn't use the single constructor version which has three default |
| // arguments. It seems the number of the default argument with lambda is |
| // limited. |
| CastOptimizer(std::function<void (SILInstruction *I, ValueBase *V)> ReplaceInstUsesAction, |
| std::function<void (SILInstruction *)> EraseAction = [](SILInstruction*){}) |
| : CastOptimizer(ReplaceInstUsesAction, EraseAction, [](){}, [](){}) {} |
| |
| /// 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, |
| CastConsumptionKind ConsumptionKind, |
| 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<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 bool isExpect(Operand *Use) { |
| if (auto *BI = dyn_cast<BuiltinInst>(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<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(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<SILInstruction *> &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 |