//===--- Reachability.h ---------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 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 // //===----------------------------------------------------------------------===// /// /// Reachability data flow analysis using a path-discovery worklist. For /// efficient data flow propagation based on a single SSA value and its uses. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H #define SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H #include "swift/SIL/BasicBlockBits.h" #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILSuccessor.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" namespace swift { /// Pessimistic, non-iterative data flow for analyzing backward reachability /// from a set of last uses to their dominating def or nearest barrier. /// /// Intended for frequently called utilities where minimizing the cost of data /// flow is more important than analyzing reachability across loops. Expected to /// visit very few blocks because barriers often occur close to a last use. /// /// BlockReachability { /// // True if the beginning of \p block is reachable. /// // Typically a BasicBlockSet wrapper. /// bool hasReachableBegin(SILBasicBlock *block); /// /// // Mark the beginning of a block reachable. Only called once per block. /// // Typically a BasicBlockSet wrapper. /// void markReachableBegin(SILBasicBlock *block); /// /// // Mark the end of a block reachable. Only called once per block. /// // Typically a BasicBlockSet wrapper. /// void markReachableEnd(SILBasicBlock *block); /// /// // Return true if \p inst is a barrier. Called once for each reachable /// // instruction, assuming that each lastUsePoint is itself a barrier. /// // Used by the data flow client to perform additional book-keeping, /// // such as recording debug_value instructions. /// bool checkReachableBarrier(SILInstruction *inst); /// /// // Return true if any of \p block's phis are barriers. /// bool checkReachablePhiBarrier(SILBasicBlock *block); /// }; template class BackwardReachability { SILFunction *function; BlockReachability &reachableBlocks; BasicBlockWorklist cfgWorklist; bool solved = false; public: BackwardReachability(SILFunction *function, BlockReachability &reachableBlocks) : function(function), reachableBlocks(reachableBlocks), cfgWorklist(function) {} // Initialize data flow starting points before running solveBackward. void initLastUse(SILInstruction *lastUsePoint) { assert(!solved && "adding last use after solving!?"); auto *lastUseBlock = lastUsePoint->getParent(); auto *previous = lastUsePoint->getPreviousInstruction(); if (!previous || canReachBlockBegin(previous)) { pushPreds(lastUseBlock); } } // Data flow "meet": intersection of successor reachability. void solveBackward() { while (SILBasicBlock *block = cfgWorklist.popAndForget()) { if (!meetOverSuccessors(block)) continue; reachableBlocks.markReachableEnd(block); if (canReachBlockBegin(block->getTerminator())) { pushPreds(block); } } solved = true; } protected: BackwardReachability(BackwardReachability const &) = delete; BackwardReachability &operator=(BackwardReachability const &) = delete; // Perform a "meet" over successor begin reachability. // Return true if \p predecessor's end is pessimistically reachable. // // Meet: // ReachableEnd(predecessor) := intersection(ReachableBegin, successors) bool meetOverSuccessors(SILBasicBlock *block) { return llvm::all_of(block->getSuccessorBlocks(), [this](auto *successor) { return reachableBlocks.hasReachableBegin(successor); }); } // Local data flow. Computes the block's flow function. bool canReachBlockBegin(SILInstruction *lastReachablePoint) { do { if (reachableBlocks.checkReachableBarrier(lastReachablePoint)) return false; lastReachablePoint = lastReachablePoint->getPreviousInstruction(); } while (lastReachablePoint); return true; } // Propagate global data flow from \p succBB to its predecessors. void pushPreds(SILBasicBlock *succBB) { if (succBB->hasPhi()) if (reachableBlocks.checkReachablePhiBarrier(succBB)) return; reachableBlocks.markReachableBegin(succBB); for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) { cfgWorklist.pushIfNotVisited(predBB); } } }; /// Iterative, optimistic data flow for analyzing backward reachability from a /// set of gens to their dominating def or nearest barrier. /// /// Uses a worklist approach to avoid extraneous iteration. Additionally, /// avoids analyzing reachability in blocks which are known to be unreachable. /// /// interface Effects { /// /// The effect, if any, of the specified instruction. /// Effect effectForInstruction(SILInstruction *); /// /// /// The effect, if any, of the phis of the specified block. /// Effect effectForPhi(SILBasicBlock *); /// /// /// The uses from which reachability will begin. /// iterable gens(); /// } template class IterativeBackwardReachability final { public: /// How an instruction or phi can modify the state of a block. /// /// NoEffect - the state is unmodified /// Kill - the state becomes unreachable /// Gen - the state becomes reachable struct Effect final { enum class Value { NoEffect, Kill, Gen, }; Value value; Effect(Value value) : value(value) {} Effect() : value(Value::NoEffect) {} static Effect NoEffect() { return {Value::NoEffect}; } static Effect Kill() { return {Value::Kill}; } static Effect Gen() { return {Value::Gen}; } bool isNoEffect() const { return value == Value::NoEffect; } /// Form the effect equivalent to first applying this effect and then /// applying the specified effect. Effect composing(Effect other) const { return other.isNoEffect() ? *this : other; } explicit operator bool() const { return !isNoEffect(); } bool operator==(const Effect &other) const { return value == other.value; } bool operator!=(const Effect &other) const { return value != other.value; } }; /// How reachable a point in the function is: /// - Unreachable: Some path from a gen to here passes through a kill. /// - Reachable: No path from a gen point to here passes through a kill. /// - Unknown: This point is the unexplored end of a block which contains a /// gen. /// /// These states form a simple lattice: /// /// Unknown (top) /// | /// Reachable /// | /// Unreachable (bottom) /// /// During initialization, certain points may be set to have the Unknown /// state. After that point, all blocks which have been discovered are /// optimistically assumed to have Reachable begin and end states until it is /// recorded otherwise. struct State final { enum class Value { Unreachable, Reachable, Unknown, }; Value value; static State Unreachable() { return {Value::Unreachable}; } static State Reachable() { return {Value::Reachable}; } static State Unknown() { return {Value::Unknown}; } State meet(State other) { return value < other.value ? *this : other; } State apply(Effect effect) { switch (effect.value) { case Effect::Value::NoEffect: return *this; case Effect::Value::Kill: return Unreachable(); case Effect::Value::Gen: return Reachable(); }; }; bool operator==(const State &other) { return value == other.value; } bool operator!=(const State &other) { return value != other.value; } }; /// The output of the dataflow. class Result final { /// Blocks whose instructions+phis are summarized as gen. BasicBlockSet genBlocks; /// Blocks whose instructions+phis are summarized as kill. BasicBlockSet killBlocks; /// Blocks whose ends are in the ::Unreachable state. BasicBlockSet unreachableEndBlocks; /// Blocks whose ends are in the ::Unknown state. BasicBlockSet unknownEndBlocks; public: /// The blocks found between the gens and the initialBlocks into which /// reachability may extend. BasicBlockSetVector discoveredBlocks; /// The sublist of gens which are killed within the blocks where they occur. llvm::SmallSetVector localGens; /// Construct a result for running IterativeBackwardReachability in a given /// function. Result(SILFunction *function) : genBlocks(function), killBlocks(function), unreachableEndBlocks(function), unknownEndBlocks(function), discoveredBlocks(function){}; /// The previously recorded end state of the specified block. State getEndStateForBlock(SILBasicBlock *block); /// The begin state of the specified block. State getBeginStateForBlock(SILBasicBlock *block) { auto endState = getEndStateForBlock(block); return transferEndToBegin(block, endState); } /// The summary effect of the specified block on reachability, as previously /// recorded. Effect getEffectForBlock(SILBasicBlock *block); /// Record the summary effect of the specified block. template void setEffectForBlock(SILBasicBlock *block, Effect effect); /// Transfer the specified block's provided end state to its begin state by /// applying its effect. State transferEndToBegin(SILBasicBlock *block, State state) { auto effect = getEffectForBlock(block); return state.apply(effect); } /// During initialization, set the specified block's initial state to be /// ::Unknown. void initializeEndStateToUnknown(SILBasicBlock *block) { unknownEndBlocks.insert(block); } /// Record a transition (down-lattice) of the indicated block to the /// specified state. bool setEndStateforBlock(SILBasicBlock *block, State state); private: Result(Result const &) = delete; Result &operator=(Result const &) = delete; }; /// Construct a dataflow for the specified function to run from the gens /// provided by \p effects to the specified \p initialBlocks. So that the /// result structure can be owned by the caller, it is taken by reference /// here. /// /// If \p initialBlocks is empty, the dataflow may run up to the begin of the /// function. IterativeBackwardReachability(SILFunction *function, ArrayRef initialBlocks, Effects &effects, Result &result) : function(function), initialBlocks(function), effects(effects), result(result), dataflowWorklist(function) { for (auto *block : initialBlocks) { this->initialBlocks.insert(block); } } /// Convenience constructor to pass a single initial block. static IterativeBackwardReachability untilInitialBlock(SILFunction *function, SILBasicBlock *initialBlock, Effects &effects, Result &result) { using InitialBlocks = ArrayRef; InitialBlocks initialBlocks = initialBlock ? InitialBlocks(initialBlock) : InitialBlocks(); return IterativeBackwardReachability(function, initialBlocks, effects, result); } /// Step 1: Prepare to run the global dataflow: discover and summarize the /// blocks in the relevant region. /// /// Effects are queried in order to summarize. void initialize(); /// Step 2 (Optional): Add kills beyond those that were already added during /// initialization, requerying effects as needed. void addKill(SILInstruction *instruction); /// Step 3: Propagate reachability through the blocks in the identified /// region. void solve(); /// Step 4 (Optional): Visit each barrier instruction, phi, and block. /// /// Effects are requeried. /// /// A barrier block here means the target block of a barrier edge. /// /// interface Visitor { /// void visitBarrierInstruction(SILInstruction *) /// void visitBarrierPhi(SILBasicBlock *) /// void visitBarrierBlock(SILBasicBlock *) /// void visitInitialBlock(SILBasicBlock *) /// } template void findBarriers(Visitor &visitor); private: /// How far along the dataflow is. enum class Stage { /// The dataflow has not done any work. Unstarted, /// Performing local, sub-block dataflow from gens, determining which gens /// are local to and should be stored in \p localGens. Initializing, /// Discovering blocks into which reachability may extend and lazily doing /// local dataflows of those blocks as they are seen. SolvingLocal, /// Discovery has completed and local dataflow has been performed for all /// discovered blocks. At this point, clients are free to examine the /// Result's \p discoveredBlocks add more kills. SolvedLocal, /// Running the iterative dataflow, draining the worklist of blocks which /// are unreachable-at-begin. SolvingGlobal, /// The dataflow has finished. SolvedGlobal, }; /// The function in which the dataflow will run. SILFunction *function; /// The blocks beyond which the dataflow will not propagate. BasicBlockSet initialBlocks; /// Input to the dataflow. Effects &effects; /// Output from the dataflow. Result &result; /// The worklist for the global data flow. It consists of blocks whose begin /// states have been found to be unreachable; the unreachable-at-begin-ness of /// each such block is to be propagated into unreachable-at-end-ness of its /// predecessors. BasicBlockWorklist dataflowWorklist; /// Current activity of the dataflow. Stage stage = Stage::Unstarted; /// Whether dataflow continues beyond this block. bool stopAtBlock(SILBasicBlock *block) { return initialBlocks.contains(block) || &*function->begin() == block; } /// Form the meet of the end state of the provided predecessor with the begin /// states of all its successors. State meetOverSuccessors(SILBasicBlock *predecessor); /// Whether the first (non-NoEffect) effect before the specified instruction /// is of the indicated kind. bool findLocalEffectBefore(SILInstruction *from, Effect target); /// Summarize the effect of the block. Effect summarizeLocalEffect(SILBasicBlock *block); /// Carry out the portion of initialization corresponding to a single gen. void initializeFromGen(SILInstruction *gen); /// Propagates the unreachable-at-begin-ness of the specified block into /// unreachable-at-end-ness of its predecessors. void propagateIntoPredecessors(SILBasicBlock *successor); /// Find and visit a single barrier instruction, phi, or block. Returns true /// if one is found. /// /// A barrier block here means the target block of a barrier edge. /// /// interface Visitor { /// void visitBarrierInstruction(SILInstruction *) /// void visitBarrierPhi(SILBasicBlock *) /// void visitBarrierBlock(SILBasicBlock *) /// void visitInitialBlock(SILBasicBlock *) /// } template bool findBarrier(SILInstruction *from, SILBasicBlock *block, Visitor &visitor); }; //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability - Initialization //===----------------------------------------------------------------------===// /// Prepare the dataflow to run. /// /// Simultaneously (1) discover the blocks which could be reached and within /// which consequently the data flow must run and (2) summarize the local /// effect of each block for use by the dataflow. /// /// Starting from the gens, find all blocks which might be reached up to and /// including the initialBlocks. Summarize the effects of these blocks along /// the way. template void IterativeBackwardReachability::initialize() { assert(stage == Stage::Unstarted); stage = Stage::Initializing; for (auto *gen : effects.gens()) { initializeFromGen(gen); } stage = Stage::SolvingLocal; /// Thanks to StackList's iterator guarantees (BasicBlockSetVector uses /// StackList's iterator), the iterator won't be invalidated by the /// modification that is done within the loop. for (auto iterator = result.discoveredBlocks.begin(); iterator != result.discoveredBlocks.end(); ++iterator) { auto *block = *iterator; auto effect = summarizeLocalEffect(block); result.template setEffectForBlock(block, effect); if (effect == Effect::Kill()) { // This block is a kill, so it is unreachable at its begin. Propagate // that unreachable-at-begin-ness to unreachable-at-end-ness of its // predecessors, if any of them are ever discovered. dataflowWorklist.push(block); // This block is a kill, so dataflow can't propagate a reachable state // through this block to its predecessors. Don't add its predecessors // to the list of discoverable blocks--they can't be reachable by way of // this block. Note though that they may become reachable through other // adjacent successors. continue; } if (stopAtBlock(block)) { // If dataflow is to stop at this block, it mustn't propagate a reachable // state through this block to its predecessors. continue; } for (auto *predecessor : block->getPredecessorBlocks()) result.discoveredBlocks.insert(predecessor); } stage = Stage::SolvedLocal; } /// Initialize the dataflow for a single gen from effect.gens. /// /// Summarize and record the local effect of the partial instruction range /// starting at gen and running to the begin of the block. template void IterativeBackwardReachability::initializeFromGen( SILInstruction *gen) { auto *block = gen->getParent(); if (findLocalEffectBefore(gen, Effect::Kill())) { // If there is a local kill before this gen, then it is a local gen. result.localGens.insert(gen); } else { result.template setEffectForBlock(block, Effect::Gen()); // Only add blocks whose begins are reached to the list of blocks which // must participate in the global dataflow. result.discoveredBlocks.insert(block); // When considering gen in isolation, we don't have enough information to // decide whether it is reachable. It may be reachable, from some other // gen, but it may not be. Set its state to ::Unknown, the top state of the // lattice. During the dataflow, its state may be updated downwards in the // lattice. result.initializeEndStateToUnknown(block); } } /// Summarize the effect of the block. /// /// Compose all the effects in the block, starting from the last instruction and /// working backwards through the first instruction and the phi if any. template typename IterativeBackwardReachability::Effect IterativeBackwardReachability::summarizeLocalEffect( SILBasicBlock *block) { Effect runningEffect; for (auto &instruction : llvm::reverse(*block)) { auto effect = effects.effectForInstruction(&instruction); runningEffect = runningEffect.composing(effect); } if (block->hasPhi()) { auto effect = effects.effectForPhi(block); runningEffect = runningEffect.composing(effect); } return runningEffect; } /// Whether an effect of the indicated kind occurs before the specified /// instruction. /// /// Scan the effects in the block backwards from the specified instruction. If /// any effect along the way is of the indicated kind, return true. template bool IterativeBackwardReachability::findLocalEffectBefore( SILInstruction *from, Effect target) { assert(target != Effect::NoEffect()); for (auto *instruction = from->getPreviousInstruction(); instruction; instruction = instruction->getPreviousInstruction()) { auto effect = effects.effectForInstruction(instruction); if (effect == target) return true; } auto *block = from->getParent(); if (block->hasPhi()) { auto effect = effects.effectForPhi(block); if (effect == target) return true; } return false; } //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability - Modification //===----------------------------------------------------------------------===// /// Record a new kill. /// /// To be called only after initialization. template void IterativeBackwardReachability::addKill( SILInstruction *instruction) { // TODO: Trim discovered set if possible. assert(stage == Stage::SolvedLocal); auto *block = instruction->getParent(); // If the kill isn't even in a discovered block, it has no effect. if (!result.discoveredBlocks.contains(block)) return; if (result.getEffectForBlock(block) != Effect::Gen()) { // If the block wasn't previously a gen block, without further work, we // already know that adding a kill to the block makes it a kill block. result.template setEffectForBlock(block, Effect::Kill()); return; } // The block was previously a gen block, so the new kill may or may not make // it a kill block; it depends on whether the new kill is above the gen which // made the block a gen. We could resummarize the block, starting from the // new kill. As an optimization, instead, we just walk until we find a gen. // Search for a previous gen. if (findLocalEffectBefore(instruction, Effect::Gen())) // If one is found, the new kill doesn't alter the block's effect. return; // Since no previous gen was found, the block is now a kill. result.template setEffectForBlock(block, Effect::Kill()); } //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability - Solving //===----------------------------------------------------------------------===// /// Iteratively solve the global dataflow. /// /// Drain the worklist which consists of blocks each of whose begins are /// unreachable, propagating that unreachable-at-begin-ness into /// unreachable-at-end-ness of its predecessors. template void IterativeBackwardReachability::solve() { assert(stage == Stage::SolvedLocal); stage = Stage::SolvingGlobal; // Now that all discovered blocks have been summarized, seed the worklist // with those discovered blocks which we can already tell are unreachable // at begin. for (auto *block : result.discoveredBlocks) { auto endState = meetOverSuccessors(block); if (result.setEndStateforBlock(block, endState)) { // The end state was updated. See whether that resulted in the begin // state becoming unreachable. auto beginState = result.getBeginStateForBlock(block); if (beginState == State::Unreachable()) { // The begin state is now unreachable. Add the block to the worklist // so that its unreachable-at-begin-ness gets propagated to // unreachable-at-end-ness of its predecessors. dataflowWorklist.pushIfNotVisited(block); } } } // Drain the worklist. while (auto *block = dataflowWorklist.popAndForget()) { // Propagate block's unreachable-at-begin-ness into // unreachable-at-end-ness of its predecessors. propagateIntoPredecessors(block); } stage = Stage::SolvedGlobal; } /// Propagates the unreachable-at-begin-ness of the specified block into /// unreachable-at-end-ness of its predecessors. /// /// If that new unreachable-at-end-ness of any predecessor results in that /// predecessor being newly unreachable at begin, add the predecessor to the /// worklist. /// /// Called with each block from the worklist as it is drained. A block is added /// to the worklist only when it is known to be unreachable at begin; /// consequently, when forming the meet of that block's begin state with its /// predecessor's end state, we can skip requerying the block's end state--it /// must be ::Unreachable. template void IterativeBackwardReachability::propagateIntoPredecessors( SILBasicBlock *successor) { // State isn't tracked above the blocks dataflow stops at. Don't propagate // state changes into its predecessors. if (stopAtBlock(successor)) return; assert(result.getBeginStateForBlock(successor) == State::Unreachable() && "propagating unreachability into predecessors of block whose begin is " "reachable?!"); for (auto *predecessor : successor->getPredecessorBlocks()) { // Blocks are added to the worklist whenever they are found to be // unreachable at begin. Specifically, no checks are done at that time that // the block's predecessors are discovered. Check now. if (!result.discoveredBlocks.contains(predecessor)) continue; auto oldEndState = result.getEndStateForBlock(predecessor); auto newEndState = State::Unreachable().meet(oldEndState); if (result.setEndStateforBlock(predecessor, newEndState)) { // The end state of predecessor was changed. Its begin state may be newly // ::Unreachable. if (result.getBeginStateForBlock(predecessor) == State::Unreachable()) { // Propagate predecessor's new unreachable-at-begin-ness to // unreachable-at-end-ness of _its_ predecessors. dataflowWorklist.pushIfNotVisited(predecessor); } } } } template typename IterativeBackwardReachability::State IterativeBackwardReachability::meetOverSuccessors( SILBasicBlock *predecessor) { auto state = result.getEndStateForBlock(predecessor); for (auto *successor : predecessor->getSuccessorBlocks()) { state = state.meet(result.getBeginStateForBlock(successor)); } return state; } //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability - Barrier Discovery //===----------------------------------------------------------------------===// /// Backwards walk discovered blocks and partial blocks starting at gens, /// visiting barrier instructions, phis, and blocks. /// /// interface Visitor { /// /// Invoked with an instruction which is a barrier. /// void visitBarrierInstruction(SILInstruction *) /// /// /// Invoked with a block one of whose phi arguments is a barrier. /// void visitBarrierPhi(SILBasicBlock *) /// /// /// Invoked with target of an edge which is a barrier. /// /// /// /// Given a block P only some of whose successors S are /// /// reachable-at-begin, an edge E from P to S is a barrier. This /// /// function is invoked for each such edge E with the block S. Thanks /// /// to the lack of critical edges in SIL, that uniquely identifies the /// /// edge as the only edge to S. /// /// /// /// Additionally, this may be invoked with the effective def block if /// /// no barriers are found between the gens and it. /// void visitBarrierBlock(SILBasicBlock *) /// /// /// Invoked with either the function entry block or one of the specified /// /// initial blocks. /// void visitInitialBlock(SILBasicBlock *) /// } template template void IterativeBackwardReachability::findBarriers(Visitor &visitor) { assert(stage == Stage::SolvedGlobal); for (auto *block : result.discoveredBlocks) { switch (result.getEndStateForBlock(block).value) { case State::Value::Reachable: findBarrier(&block->back(), block, visitor); break; case State::Value::Unreachable: for (auto *successor : block->getSuccessorBlocks()) { if (result.getBeginStateForBlock(successor) == State::Reachable()) { visitor.visitBarrierBlock(successor); } } break; case State::Value::Unknown: // A block which contained a gen which was never overwritten by global // data flow. break; } } for (auto *gen : effects.gens()) { bool foundBarrier = findBarrier(gen->getPreviousInstruction(), gen->getParent(), visitor); assert((foundBarrier || !result.localGens.contains(gen)) && "local gen without in-block kill?!"); (void)foundBarrier; } } /// Find a single barrier, starting from the specified instruction (nullable) /// within the indicated block (non-nullable) and visit it. /// /// Return whether a barrier was found. /// /// interface Visitor { /// /// Invoked with an instruction which is a barrier. /// void visitBarrierInstruction(SILInstruction *) /// /// /// Invoked with a block one of whose phi arguments is a barrier. /// void visitBarrierPhi(SILBasicBlock *) /// /// /// Invoked with target of an edge which is a barrier. /// /// /// /// Given a block P only some of whose successors S are /// /// reachable-at-begin, an edge E from P to S is a barrier. This /// /// function is invoked for each such edge E with the block S. Thanks /// /// to the lack of critical edges in SIL, that uniquely identifies the /// /// edge as the only edge to S. /// /// /// /// Additionally, this may be invoked with the effective def block if /// /// no barriers are found between the gens and it. /// void visitBarrierBlock(SILBasicBlock *) /// /// /// Invoked with either the function entry block or one of the specified /// /// initial blocks. /// void visitInitialBlock(SILBasicBlock *) /// } template template bool IterativeBackwardReachability::findBarrier(SILInstruction *from, SILBasicBlock *block, Visitor &visitor) { for (auto *instruction = from; instruction; instruction = instruction->getPreviousInstruction()) { auto effect = effects.effectForInstruction(instruction); if (!effect) continue; if (effect == Effect::Gen()) { continue; } // effect == Effect::Kill visitor.visitBarrierInstruction(instruction); return true; } if (block->hasPhi()) { if (auto effect = effects.effectForPhi(block)) { if (effect == Effect::Kill()) { visitor.visitBarrierPhi(block); return true; } } } assert(result.getEffectForBlock(block) != Effect::Kill()); if (stopAtBlock(block)) { visitor.visitInitialBlock(block); return true; } return false; } //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability::Result - State //===----------------------------------------------------------------------===// /// The previously recorded end state of the specified block. template typename IterativeBackwardReachability::State IterativeBackwardReachability::Result::getEndStateForBlock( SILBasicBlock *block) { if (!discoveredBlocks.contains(block)) return State::Unreachable(); if (unreachableEndBlocks.contains(block)) return State::Unreachable(); if (unknownEndBlocks.contains(block)) return State::Unknown(); return State::Reachable(); } /// Record a transition of the indicated block to the specified state. /// /// States must transition down the State lattice. /// /// The state is recorded in: /// - unreachableEndBlocks; blocks contained here are ::Unreachable /// - unknownEndBlocks; blocks contained here are ::Unknown /// Blocks that are in neither are ::Reachable. template bool IterativeBackwardReachability::Result::setEndStateforBlock( SILBasicBlock *block, State state) { switch (state.value) { case State::Value::Unreachable: { auto previouslyUnknown = unknownEndBlocks.contains(block); unknownEndBlocks.erase(block); auto notPreviouslyUnreachable = unreachableEndBlocks.insert(block); assert(!previouslyUnknown || notPreviouslyUnreachable && "block was previously in both unknown and unreachable?!"); return previouslyUnknown || notPreviouslyUnreachable; } case State::Value::Reachable: { auto previouslyUnknown = unknownEndBlocks.contains(block); unknownEndBlocks.erase(block); assert(!unreachableEndBlocks.contains(block) && "transitioning up lattice?! unreachable->reachable"); return previouslyUnknown; } case State::Value::Unknown: { auto notPreviouslyUnknown = unknownEndBlocks.insert(block); assert(!unreachableEndBlocks.contains(block) && "transitioning up lattice?! unreachable->unknown"); assert(!notPreviouslyUnknown && "transitioning up lattice?! reachable->unknown"); return notPreviouslyUnknown; } } } //===----------------------------------------------------------------------===// // MARK: IterativeBackwardReachability::Result - Effects //===----------------------------------------------------------------------===// /// The summary effect of the specified block on reachability, as previously /// recorded. template typename IterativeBackwardReachability::Effect IterativeBackwardReachability::Result::getEffectForBlock( SILBasicBlock *block) { if (genBlocks.contains(block)) return Effect::Gen(); if (killBlocks.contains(block)) return Effect::Kill(); return Effect::NoEffect(); } /// Record the summary effect of the specified block. /// /// If it is statically known that the block has not already had its effect set /// to an incompatible value, we can avoid clearing the old effect. For /// example, if this is the first time a block's state has been set, or if we /// are setting a block's state to ::Gen and we already know it isn't ::Kill. template template void IterativeBackwardReachability::Result::setEffectForBlock( SILBasicBlock *block, Effect effect) { switch (effect.value) { case Effect::Value::NoEffect: if (!SkipOverwrite) { genBlocks.erase(block); killBlocks.erase(block); } return; case Effect::Value::Gen: genBlocks.insert(block); if (!SkipOverwrite) killBlocks.erase(block); return; case Effect::Value::Kill: killBlocks.insert(block); if (!SkipOverwrite) genBlocks.erase(block); return; } } //===----------------------------------------------------------------------===// // MARK: findBarriersBackward //===----------------------------------------------------------------------===// using llvm::ArrayRef; using llvm::function_ref; struct ReachableBarriers final { /// Instructions which are barriers. llvm::SmallVector instructions; /// Blocks one of whose phis is a barrier. llvm::SmallVector phis; /// Boundary edges; edges such that /// (1) the target block is reachable-at-begin /// (2) at least one adjacent edge's target is not reachable-at-begin. llvm::SmallVector edges; /// Terminal blocks that were reached; blocks such that /// (1) the block is reachable-at-begin /// (2) it is either the function's entry block or one of the blocks passed /// to findBarriersBackward's \p initialBlocks parameter. llvm::SmallVector initialBlocks; ReachableBarriers() {} ReachableBarriers(ReachableBarriers const &) = delete; ReachableBarriers &operator=(ReachableBarriers const &) = delete; }; /// Walk backwards from the specified \p roots through at the earliest \p /// initialBlocks to populate \p barriers by querying \p isBarrier along the /// way. /// /// If \p initialBlocks is empty, dataflow continues to the begin of the /// function. void findBarriersBackward(ArrayRef roots, ArrayRef initialBlocks, SILFunction &function, ReachableBarriers &barriers, function_ref isBarrier); } // end namespace swift #endif