|
| 1 | +//===--- Reachability.h ---------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | +/// |
| 13 | +/// Reachability data flow analysis using a path-discovery worklist. For |
| 14 | +/// efficient data flow propagation based on a single SSA value and its uses. |
| 15 | +/// |
| 16 | +/// TODO: Add an optimistic data flow for more aggresive optimization: |
| 17 | +/// - Add another set for blocks reachable by barriers |
| 18 | +/// - Change the meet operation to a union |
| 19 | +/// - Propagate past barriers up to the SSA def |
| 20 | +/// - Iterate to a fix-point. |
| 21 | +/// |
| 22 | +//===----------------------------------------------------------------------===// |
| 23 | + |
| 24 | +#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H |
| 25 | +#define SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H |
| 26 | + |
| 27 | +#include "swift/SIL/BasicBlockDatastructures.h" |
| 28 | +#include "swift/SIL/SILBasicBlock.h" |
| 29 | + |
| 30 | +namespace swift { |
| 31 | + |
| 32 | +/// Pessimistic, non-iterative data flow for analyzing backward reachability |
| 33 | +/// from a set of last uses to their dominating def or nearest barrier. |
| 34 | +/// |
| 35 | +/// Intended for frequently called utilities where minimizing the cost of data |
| 36 | +/// flow is more important than analyzing reachability across loops. Expected to |
| 37 | +/// visit very few blocks because barriers often occur close to a last use. |
| 38 | +/// |
| 39 | +/// BlockReachability { |
| 40 | +/// // True if the beginning of \p block is reachable. |
| 41 | +/// // Typically a BasicBlockSet wrapper. |
| 42 | +/// bool hasReachableBegin(SILBasicBlock *block); |
| 43 | +/// |
| 44 | +/// // Mark the beginning of a block reachable. Only called once per block. |
| 45 | +/// // Typically a BasicBlockSet wrapper. |
| 46 | +/// boid markReachableBegin(SILBasicBlock *block); |
| 47 | +/// |
| 48 | +/// // Mark the end of a block reachable. Only called once per block. |
| 49 | +/// // Typically a BasicBlockSet wrapper. |
| 50 | +/// void markReachableEnd(SILBasicBlock *block); |
| 51 | +/// |
| 52 | +/// // Return true if \p inst is a barrier. Called once for each reachable |
| 53 | +/// // instruction, assuming that each lastUse is itself a barrier. |
| 54 | +/// // Used by the data flow client to perform additional book-keeping, |
| 55 | +/// // such as recording debug_value instructions. |
| 56 | +/// bool checkReachableBarrier(SILInstruction *inst); |
| 57 | +/// }; |
| 58 | +template <typename BlockReachability> |
| 59 | +class BackwardReachability { |
| 60 | + SILFunction *function; |
| 61 | + BlockReachability &reachableBlocks; |
| 62 | + BasicBlockWorklist cfgWorklist; |
| 63 | + |
| 64 | +public: |
| 65 | + BackwardReachability(SILFunction *function, |
| 66 | + BlockReachability &reachableBlocks) |
| 67 | + : function(function), reachableBlocks(reachableBlocks), |
| 68 | + cfgWorklist(function) {} |
| 69 | + |
| 70 | + // Initialize data flow starting points before running solveBackward. |
| 71 | + void initLastUse(SILInstruction *lastUsePoint) { |
| 72 | + auto *lastUseBlock = lastUsePoint->getParent(); |
| 73 | + if (canReachBlockBegin(lastUsePoint)) { |
| 74 | + pushPreds(lastUseBlock); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // Data flow "meet": interesection of successor reachability. |
| 79 | + void solveBackward() { |
| 80 | + while (SILBasicBlock *block = cfgWorklist.popAndForget()) { |
| 81 | + if (!meetOverSuccessors(block)) |
| 82 | + continue; |
| 83 | + |
| 84 | + reachableBlocks.markReachableEnd(block); |
| 85 | + |
| 86 | + if (canReachBlockBegin(block->getTerminator())) { |
| 87 | + pushPreds(block); |
| 88 | + } |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | +protected: |
| 93 | + BackwardReachability(BackwardReachability const &) = delete; |
| 94 | + BackwardReachability &operator=(BackwardReachability const &) = delete; |
| 95 | + |
| 96 | + // Perform a "meet" over successor begin reachability. |
| 97 | + // Return true if \p predecessor's end is pessimistically reachable. |
| 98 | + // |
| 99 | + // Meet: |
| 100 | + // ReachableEnd(predecessor) := intersection(ReachableBegin, successors) |
| 101 | + bool meetOverSuccessors(SILBasicBlock *block) { |
| 102 | + return llvm::all_of(block->getSuccessorBlocks(), [this](auto *successor) { |
| 103 | + return reachableBlocks.hasReachableBegin(successor); |
| 104 | + }); |
| 105 | + } |
| 106 | + |
| 107 | + // Local data flow. Computes the block's flow function. |
| 108 | + bool canReachBlockBegin(SILInstruction *lastReachablePoint) { |
| 109 | + do { |
| 110 | + if (reachableBlocks.checkReachableBarrier(lastReachablePoint)) |
| 111 | + return false; |
| 112 | + lastReachablePoint = lastReachablePoint->getPreviousInstruction(); |
| 113 | + } while (lastReachablePoint); |
| 114 | + return true; |
| 115 | + } |
| 116 | + |
| 117 | + // Propagate global data flow from \p succBB to its predecessors. |
| 118 | + void pushPreds(SILBasicBlock *succBB) { |
| 119 | + reachableBlocks.markReachableBegin(succBB); |
| 120 | + |
| 121 | + for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) { |
| 122 | + cfgWorklist.pushIfNotVisited(predBB); |
| 123 | + } |
| 124 | + } |
| 125 | +}; |
| 126 | + |
| 127 | +} // end namespace swift |
| 128 | + |
| 129 | +#endif |
0 commit comments