|
| 1 | +//===--- BasicBlockRange.swift - a range of basic blocks ------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2022 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 | +import SIL |
| 14 | + |
| 15 | +/// A range of basic blocks. |
| 16 | +/// |
| 17 | +/// The `BasicBlockRange` defines a range from a dominating "begin" block to one or more "end" blocks. |
| 18 | +/// The range is "exclusive", which means that the "end" blocks are not part of the range. |
| 19 | +/// |
| 20 | +/// The `BasicBlockRange` is in the same spirit as a linear range, but as the control flow is a graph |
| 21 | +/// and not a linear list, there can be "exit" blocks from within the range. |
| 22 | +/// |
| 23 | +/// One or more "potential" end blocks can be inserted. |
| 24 | +/// Though, not all inserted blocks end up as "end" blocks. |
| 25 | +/// |
| 26 | +/// There are several kind of blocks: |
| 27 | +/// * begin: it is a single block which dominates all blocks of the range |
| 28 | +/// * range: all blocks from which there is a path from the begin block to any of the end blocks |
| 29 | +/// * ends: all inserted blocks which are at the end of the range |
| 30 | +/// * exits: all successor blocks of range blocks which are not in the range themselves |
| 31 | +/// * interiors: all inserted blocks which are not end blocks. |
| 32 | +/// |
| 33 | +/// In the following example, let's assume `B` is the begin block and `I1`, `I2` and `I3` |
| 34 | +/// were inserted as potential end blocks: |
| 35 | +/// |
| 36 | +/// B |
| 37 | +/// / \ |
| 38 | +/// I1 I2 |
| 39 | +/// / \ |
| 40 | +/// I3 X |
| 41 | +/// |
| 42 | +/// Then `I1` and `I3` are "end" blocks. `I2` is an interior block and `X` is an exit block. |
| 43 | +/// The range consists of `B` and `I2`. Note that the range does not include `I1` and `I3` |
| 44 | +/// because it's an _exclusive_ range. |
| 45 | +/// |
| 46 | +/// This type should be a move-only type, but unfortunately we don't have move-only |
| 47 | +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to |
| 48 | +/// destruct this data structure, e.g. in a `defer {}` block. |
| 49 | +struct BasicBlockRange : CustomStringConvertible, CustomReflectable { |
| 50 | + |
| 51 | + /// The dominating begin block. |
| 52 | + let begin: BasicBlock |
| 53 | + |
| 54 | + /// The exclusive range, i.e. not containing the end blocks. |
| 55 | + private(set) var range: Stack<BasicBlock> |
| 56 | + |
| 57 | + /// All inserted blocks. |
| 58 | + private(set) var inserted: Stack<BasicBlock> |
| 59 | + |
| 60 | + private var insertedSet: BasicBlockSet |
| 61 | + private var worklist: BasicBlockWorklist |
| 62 | + |
| 63 | + init(begin: BasicBlock, _ context: PassContext) { |
| 64 | + self.begin = begin |
| 65 | + self.range = Stack(context) |
| 66 | + self.inserted = Stack(context) |
| 67 | + self.insertedSet = BasicBlockSet(context) |
| 68 | + self.worklist = BasicBlockWorklist(context) |
| 69 | + } |
| 70 | + |
| 71 | + /// Insert a potential end block. |
| 72 | + mutating func insert(_ block: BasicBlock) { |
| 73 | + if !insertedSet.contains(block) { |
| 74 | + insertedSet.insert(block) |
| 75 | + inserted.append(block) |
| 76 | + } |
| 77 | + if block != begin { |
| 78 | + worklist.pushIfNotVisited(contentsOf: block.predecessors) |
| 79 | + |
| 80 | + while let b = worklist.pop() { |
| 81 | + range.append(b) |
| 82 | + if b != begin { |
| 83 | + worklist.pushIfNotVisited(contentsOf: b.predecessors) |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + /// Returns true if the exclusive range contains `block`. |
| 90 | + func contains(_ block: BasicBlock) -> Bool { worklist.hasBeenPushed(block) } |
| 91 | + |
| 92 | + /// Returns true if the inclusive range contains `block`. |
| 93 | + func inclusiveRangeContains (_ block: BasicBlock) -> Bool { |
| 94 | + contains(block) || insertedSet.contains(block) |
| 95 | + } |
| 96 | + |
| 97 | + /// Returns true if the range is valid and that's iff the begin block dominates all blocks of the range. |
| 98 | + var isValid: Bool { |
| 99 | + let entry = begin.function.entryBlock |
| 100 | + return begin == entry || |
| 101 | + // If any block in the range is not dominated by `begin`, the range propagates back to the entry block. |
| 102 | + !inclusiveRangeContains(entry) |
| 103 | + } |
| 104 | + |
| 105 | + /// Returns the end blocks. |
| 106 | + var ends: LazyFilterSequence<Stack<BasicBlock>> { |
| 107 | + inserted.lazy.filter { !worklist.hasBeenPushed($0) } |
| 108 | + } |
| 109 | + |
| 110 | + /// Returns the exit blocks. |
| 111 | + var exits: LazySequence<FlattenSequence< |
| 112 | + LazyMapSequence<Stack<BasicBlock>, |
| 113 | + LazyFilterSequence<SuccessorArray>>>> { |
| 114 | + range.lazy.flatMap { |
| 115 | + $0.successors.lazy.filter { |
| 116 | + !inclusiveRangeContains($0) || $0 == begin |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + /// Returns the interior blocks. |
| 122 | + var interiors: LazyFilterSequence<Stack<BasicBlock>> { |
| 123 | + inserted.lazy.filter { contains($0) && $0 != begin } |
| 124 | + } |
| 125 | + |
| 126 | + var description: String { |
| 127 | + """ |
| 128 | + begin: \(begin.name) |
| 129 | + range: \(range) |
| 130 | + ends: \(ends) |
| 131 | + exits: \(exits) |
| 132 | + interiors: \(interiors) |
| 133 | + """ |
| 134 | + } |
| 135 | + |
| 136 | + var customMirror: Mirror { Mirror(self, children: []) } |
| 137 | + |
| 138 | + /// TODO: once we have move-only types, make this a real deinit. |
| 139 | + mutating func deinitialize() { |
| 140 | + worklist.deinitialize() |
| 141 | + inserted.deinitialize() |
| 142 | + insertedSet.deinitialize() |
| 143 | + range.deinitialize() |
| 144 | + } |
| 145 | +} |
0 commit comments