|
14 | 14 | #define SWIFT_SIL_BASICBLOCKUTILS_H |
15 | 15 |
|
16 | 16 | #include "swift/SIL/BasicBlockBits.h" |
| 17 | +#include "swift/SIL/BasicBlockData.h" |
17 | 18 | #include "swift/SIL/BasicBlockDatastructures.h" |
18 | 19 | #include "swift/SIL/SILValue.h" |
19 | 20 | #include "llvm/ADT/SetVector.h" |
@@ -125,6 +126,213 @@ class DeadEndBlocks { |
125 | 126 | void propagateNewlyReachableBlocks(unsigned startIdx); |
126 | 127 | }; |
127 | 128 |
|
| 129 | +/// A utility for detecting edges that enter a dead-end region. |
| 130 | +/// |
| 131 | +/// A dead-end region is a strongly-connected component of the CFG |
| 132 | +/// consisting solely of dead-end blocks (i.e. from which it is not |
| 133 | +/// possible to reach a function exit). The strongly-connected |
| 134 | +/// components of a CFG form a DAG: once control flow from the entry |
| 135 | +/// block has entered an SCC, it cannot return to an earlier SCC |
| 136 | +/// (because then by definition they would have to be the same SCC). |
| 137 | +/// |
| 138 | +/// Note that the interior edges of a dead-end region do not *enter* |
| 139 | +/// the region. Only edges from an earlier SCC count as edges into |
| 140 | +/// the region. |
| 141 | +/// |
| 142 | +/// For example, in this CFG: |
| 143 | +/// |
| 144 | +/// /-> bb1 -> bb2 -> return |
| 145 | +/// bb0 |
| 146 | +/// \-> bb3 -> bb4 -> bb5 -> unreachable |
| 147 | +/// ^ | |
| 148 | +/// \------/ |
| 149 | +/// |
| 150 | +/// The edge from bb0 to bb3 enters a new dead-end region, as does |
| 151 | +/// the edge from bb4 to bb5. The edge from bb4 to bb3 does not |
| 152 | +/// enter a new region because it is an internal edge of its region. |
| 153 | +/// |
| 154 | +/// Edges that enter dead-end regions are special in SIL because certain |
| 155 | +/// joint post-dominance rules are relaxed for them. For example, the |
| 156 | +/// stack does need not be consistent on different edges into a dead-end |
| 157 | +/// region. |
| 158 | +class DeadEndEdges { |
| 159 | + enum : unsigned { |
| 160 | + /// A region data value which represents that a block is unreachable |
| 161 | + /// from the entry block. |
| 162 | + UnreachableRegionData = 0, |
| 163 | + |
| 164 | + /// A region data value which represents that a block is reachable |
| 165 | + /// from the entry block but not in a dead-end region. |
| 166 | + NonDeadEndRegionData = 1, |
| 167 | + |
| 168 | + /// A value that must be added to a region index when storing it in |
| 169 | + /// a region data. |
| 170 | + /// |
| 171 | + /// This should be the smallest number such that |
| 172 | + /// (IndexOffset << IndexShift) |
| 173 | + /// is always greater than all of the special region-data values |
| 174 | + /// above. |
| 175 | + IndexOffset = 1, |
| 176 | + |
| 177 | + /// A mask which can be applied to a region to say that it contains |
| 178 | + /// a cycle. This slightly optimizes the check in isDeadEndEdge for |
| 179 | + /// the common case where regions do not have cycles. |
| 180 | + HasCycleMask = 0x1, |
| 181 | + |
| 182 | + /// The amount to shift the region index by when storing it in a |
| 183 | + /// region data. |
| 184 | + /// |
| 185 | + /// This should be the smallest number such that an arbitrary value |
| 186 | + /// left-shifted by it will not have any of the mask bits set. |
| 187 | + IndexShift = 1, |
| 188 | + }; |
| 189 | + |
| 190 | + /// An integer representing what we know about the SCC partition that |
| 191 | + /// a particular block is in. All blocks in the same region store the |
| 192 | + /// same value to make comparisons faster. |
| 193 | + /// |
| 194 | + /// Either: |
| 195 | + /// - UnreachableRegionData, representing a block that cannot be |
| 196 | + /// reached from the entry block; |
| 197 | + /// - NonDeadEndRegionData, representing a block that can be reached |
| 198 | + /// from the entry block but is not in a dead-end region; or |
| 199 | + /// - an encoded region index, representing a block that is in a |
| 200 | + /// dead-end region. |
| 201 | + /// |
| 202 | + /// A region index is a unique value in 0..<numDeadEndRegions, |
| 203 | + /// selected for a specific dead-end SCC. It is encoded by adding |
| 204 | + /// IndexOffset, left-shifting by IndexShift, and then or'ing |
| 205 | + /// in any appropriate summary bits like HasCycleMask. |
| 206 | + /// |
| 207 | + /// If regionDataForBlock isn't initialized, the function contains |
| 208 | + /// no dead-end blocks. |
| 209 | + std::optional<BasicBlockData<unsigned>> regionDataForBlock; |
| 210 | + |
| 211 | + /// The total number of dead-end regions in the function. |
| 212 | + unsigned numDeadEndRegions; |
| 213 | + |
| 214 | + static constexpr bool isDeadEndRegion(unsigned regionData) { |
| 215 | + return regionData >= (IndexOffset << IndexShift); |
| 216 | + } |
| 217 | + |
| 218 | + static unsigned getIndexFromRegionData(unsigned regionData) { |
| 219 | + assert(isDeadEndRegion(regionData)); |
| 220 | + return (regionData >> IndexShift) - IndexOffset; |
| 221 | + } |
| 222 | + |
| 223 | +public: |
| 224 | + /// Perform the analysis on the given function. An existing |
| 225 | + /// DeadEndBlocks analysis can be passed in to avoid needing to |
| 226 | + /// compute it anew. |
| 227 | + explicit DeadEndEdges(SILFunction *F, |
| 228 | + DeadEndBlocks *deadEndBlocks = nullptr); |
| 229 | + |
| 230 | + /// Return the number of dead-end regions in the function. |
| 231 | + unsigned getNumDeadEndRegions() const { |
| 232 | + return numDeadEndRegions; |
| 233 | + } |
| 234 | + |
| 235 | + /// Does the given CFG edge enter a new dead-end region? |
| 236 | + /// |
| 237 | + /// If so, return the index of the dead-end region it enters. |
| 238 | + std::optional<unsigned> |
| 239 | + entersDeadEndRegion(SILBasicBlock *srcBB, SILBasicBlock *dstBB) const { |
| 240 | + // If we didn't initialize regionDataForBlock, there are no dead-end |
| 241 | + // edges at all. |
| 242 | + if (!regionDataForBlock) |
| 243 | + return std::nullopt; |
| 244 | + |
| 245 | + auto dstRegionData = (*regionDataForBlock)[dstBB]; |
| 246 | + |
| 247 | + // If the destination block is not in a dead-end region, this is |
| 248 | + // not a dead-end edge. |
| 249 | + if (!isDeadEndRegion(dstRegionData)) return std::nullopt; |
| 250 | + |
| 251 | + unsigned dstRegionIndex = getIndexFromRegionData(dstRegionData); |
| 252 | + |
| 253 | + // If the destination block is in a region with no cycles, every edge |
| 254 | + // to it is a dead-end edge; no need to look up the source block's |
| 255 | + // region. |
| 256 | + if (!(dstRegionData & HasCycleMask)) return dstRegionIndex; |
| 257 | + |
| 258 | + // Otherwise, it's a dead-end edge if the source block is in a |
| 259 | + // different region. (That region may or may not be itself be a |
| 260 | + // dead-end region.) |
| 261 | + auto srcRegionData = (*regionDataForBlock)[srcBB]; |
| 262 | + if (srcRegionData != dstRegionData) { |
| 263 | + return dstRegionIndex; |
| 264 | + } else { |
| 265 | + return std::nullopt; |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + /// A helper class for tracking visits to edges into dead-end regions. |
| 270 | + /// |
| 271 | + /// The client is assumed to be doing a walk of the function which will |
| 272 | + /// naturally visit each edge exactly once. This set allows the client |
| 273 | + /// to track when they've processed every edge to a particular dead-end |
| 274 | + /// region and can therefore safely enter it. |
| 275 | + /// |
| 276 | + /// The set does not count edges from unreachable blocks by default. This |
| 277 | + /// matches the normal expectation that the client is doing a CFG search |
| 278 | + /// and won't try to visit edges from unreachable blocks. If you are |
| 279 | + /// walking the function in some other, e.g. by iterating the blocks, |
| 280 | + /// you must pass `true` for `includeUnreachableEdges`. |
| 281 | + class VisitingSet { |
| 282 | + const DeadEndEdges &edges; |
| 283 | + |
| 284 | + /// Stores the remaining number of edges for each dead-end region |
| 285 | + /// in the function. |
| 286 | + SmallVector<unsigned> remainingEdgesForRegion; |
| 287 | + |
| 288 | + friend class DeadEndEdges; |
| 289 | + explicit VisitingSet(const DeadEndEdges &parent, |
| 290 | + bool includeUnreachableEdges); |
| 291 | + |
| 292 | + public: |
| 293 | + /// Record that a dead-end edge to the given block was visited. |
| 294 | + /// |
| 295 | + /// Returns true if this was the last dead-end edge to the region |
| 296 | + /// containing the block. |
| 297 | + /// |
| 298 | + /// Do not call this multiple times for the same edge. Do not |
| 299 | + /// call this for an unreachable edge if you did not create the |
| 300 | + /// set including unreachable edges. |
| 301 | + bool visitEdgeTo(SILBasicBlock *destBB) { |
| 302 | + assert(edges.regionDataForBlock && |
| 303 | + "visiting dead-end edge in function that has none"); |
| 304 | + auto destRegionData = (*edges.regionDataForBlock)[destBB]; |
| 305 | + assert(isDeadEndRegion(destRegionData) && |
| 306 | + "destination block is not in a dead-end region"); |
| 307 | + |
| 308 | + auto destRegionIndex = getIndexFromRegionData(destRegionData); |
| 309 | + assert(remainingEdgesForRegion[destRegionIndex] > 0 && |
| 310 | + "no remaining dead-end edges for region; visited " |
| 311 | + "multiple times?"); |
| 312 | + |
| 313 | + auto numRemaining = --remainingEdgesForRegion[destRegionIndex]; |
| 314 | + return numRemaining == 0; |
| 315 | + } |
| 316 | + |
| 317 | + /// Return true if all of the edges have been visited. |
| 318 | + bool visitedAllEdges() const { |
| 319 | + for (auto count : remainingEdgesForRegion) { |
| 320 | + if (count) return false; |
| 321 | + } |
| 322 | + return true; |
| 323 | + } |
| 324 | + }; |
| 325 | + |
| 326 | + /// Create a counter set which can be used to count edges in the |
| 327 | + /// dead-end regions. |
| 328 | + /// |
| 329 | + /// By default, the set does not include edges from unreachable blocks. |
| 330 | + VisitingSet createVisitingSet(bool includeUnreachableEdges = false) const { |
| 331 | + return VisitingSet(*this, includeUnreachableEdges); |
| 332 | + } |
| 333 | +}; |
| 334 | + |
| 335 | + |
128 | 336 | /// Compute joint-postdominating set for \p dominatingBlock and \p |
129 | 337 | /// dominatedBlockSet found by walking up the CFG from the latter to the |
130 | 338 | /// former. |
|
0 commit comments