|
| 1 | +//===--- OwnershipLiveness.h ---------------------------------*- C++ -*----===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2023 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 | +/// Terminology: |
| 14 | +/// |
| 15 | +/// Linear lifetime: All paths through the value's definition to the |
| 16 | +/// function exit pass through exactly one lifetime-ending operation. |
| 17 | +/// |
| 18 | +/// Complete OSSA: All owned values and borrow introducers have linear lifetime. |
| 19 | +/// |
| 20 | +/// Borrow introducer: defines a guaranteed SILValue and an associated explicit |
| 21 | +/// borrow scope: begin_borrow, load_borrow, store_borrow, and reborrow. A |
| 22 | +/// "reborrow" is a guaranteed phi for which `isGuaranteedForwarding` returns |
| 23 | +/// false. (Eventually, the phi will know whether it is a reborrow based on a |
| 24 | +/// flag or subclass). Guaranteed function arguments do conceptually introduce |
| 25 | +/// borrow scopes, but their scopes are implied by the function entry and |
| 26 | +/// return points. |
| 27 | +/// |
| 28 | +/// Completing an OSSA lifetime is the process of giving an owned value or a |
| 29 | +/// borrow introducer a linear lifetime. This process may require phi creation |
| 30 | +/// whenever a nested scope involves a guaranteed phi that is not dominated by |
| 31 | +/// the outer scope's definition. A new phi will be created that ends the outer |
| 32 | +/// lifetime and begins a new lifetime in the successor block. This new phi will |
| 33 | +/// either forward an owned value or reborrow the outer borrow scope. The new |
| 34 | +/// phi's lifetime will then be recursively extended over the lifetime of the |
| 35 | +/// original guaranteed phi, which we refer to as its inner adjacent phi. |
| 36 | +/// |
| 37 | +//===----------------------------------------------------------------------===// |
| 38 | +/// |
| 39 | +/// Linear SSA liveness: |
| 40 | +/// |
| 41 | +/// - Liveness for a single "defining" owned value or borrow introducing value |
| 42 | +/// assuming the value's lifetime is already linear. |
| 43 | +/// |
| 44 | +/// - The definition dominates all use points (phis do not extend the lifetime) |
| 45 | +/// |
| 46 | +/// - Only lifetime-ending operations generate liveness |
| 47 | +/// |
| 48 | +/// - Oblivious to pointer escapes within the lifetime |
| 49 | +/// |
| 50 | +/// |
| 51 | +/// Interior SSA liveness: |
| 52 | +/// |
| 53 | +/// - Liveness for a single "defining" value with any ownership. |
| 54 | +/// |
| 55 | +/// - The definition dominates all use points (phis do not extend the lifetime) |
| 56 | +/// |
| 57 | +/// - Does not assume the current lifetime is linear. Transitively follows |
| 58 | +/// guaranteed forwarding and address uses within the current scope. |
| 59 | +/// |
| 60 | +/// - Liveness cannot extend beyond lifetime-ending operations |
| 61 | +/// (a.k.a. affine lifetimes). |
| 62 | +/// |
| 63 | +/// - Assumes inner scopes *are* linear, including borrow and address scopes |
| 64 | +/// (e.g. begin_borrow, load_borrow, begin_apply, store_borrow, begin_access) |
| 65 | +/// A callback may be used to complete inner scopes before updating liveness. |
| 66 | +/// |
| 67 | +/// - Only returns AddressUseKind::PointerEscape if one of the uses of the |
| 68 | +/// outer value has OperandOwnership::PointerEscape or |
| 69 | +/// OperandOwnership::BitwiseEscape. An inner scope's guaranteed value may |
| 70 | +/// escape without causing the outer scope's value to escape. |
| 71 | +/// |
| 72 | +/// - Insulates outer scopes from inner scope details. Maintains the |
| 73 | +/// invariant that inlining cannot pessimize optimization. |
| 74 | +/// |
| 75 | +/// - Interior SSA liveness is used to complete (linearize) an OSSA lifetime |
| 76 | +/// |
| 77 | +/// Interior liveness example: |
| 78 | +/// |
| 79 | +/// %struct = struct ... |
| 80 | +/// %f = struct_extract %s // defines a guaranteed value (%f) |
| 81 | +/// %b = begin_borrow %field |
| 82 | +/// %a = ref_element_addr %b |
| 83 | +/// _ = address_to_pointer %a |
| 84 | +/// end_borrow %b // the only interior use of %f |
| 85 | +/// |
| 86 | +/// When computing interior liveness for %f, %b is an inner scope. Because inner |
| 87 | +/// scopes are complete, the only relevant use is end_borrow %b. Despite the |
| 88 | +/// address_to_pointer instruction, %f does not escape any dependent address. |
| 89 | +/// |
| 90 | +/// Transitive SSA liveness |
| 91 | +/// |
| 92 | +/// - Similar to Interior SSA liveness, but does not assume that any lifetimes |
| 93 | +/// are linear. Transitively follows uses within inner scopes, recursively |
| 94 | +/// through nested scopes, including forwarding operations and address uses. |
| 95 | +/// |
| 96 | +/// - Much more likely to return AddressUseKind::PointerEscape |
| 97 | +/// |
| 98 | +/// Transitive liveness example: |
| 99 | +/// |
| 100 | +/// %struct = struct ... |
| 101 | +/// %f = struct_extract %s // defines a guaranteed value (%f) |
| 102 | +/// %b = begin_borrow %field |
| 103 | +/// %a = ref_element_addr %b |
| 104 | +/// _ = address_to_pointer %a // a transitive use of %f escapes |
| 105 | +/// |
| 106 | +/// When computing transitive liveness for %f, %b is an inner scope. Liveness |
| 107 | +/// does not assume that an end_borrow exists. Instead it transitively considers |
| 108 | +/// all uses of %b. As a result, %f escapes. |
| 109 | +/// |
| 110 | +/// - This header provides no interface for transitive liveness because it is no |
| 111 | +/// longer needed with complete OSSA lifetimes |
| 112 | +/// |
| 113 | +/// Extended liveness |
| 114 | +/// |
| 115 | +/// - Liveness of a single "defining" value extended beyond its lifetime-ending |
| 116 | +/// operations. |
| 117 | +/// |
| 118 | +/// - May refer to copy-extension, phi-extension, or extension over barriers |
| 119 | +/// such as access markers. |
| 120 | +/// |
| 121 | +/// - For phi-extension, the definition no longer dominates the use |
| 122 | +/// points. MultiDefPrunedLiveness must be used. Each phi is added as a new |
| 123 | +/// definition. |
| 124 | +/// |
| 125 | +/// Extended linear liveness |
| 126 | +/// |
| 127 | +/// - Extended liveness that only considers lifetime-ending operations. Assumes |
| 128 | +/// the definition already has a linear lifetime and that any phis that end |
| 129 | +/// the current lifetime also have linear lifetimes. |
| 130 | +/// |
| 131 | +/// Extended interior liveness |
| 132 | +/// |
| 133 | +/// - Like interior SSA liveness, does not assume the current lifetime is |
| 134 | +/// linear. Transitively follows guaranteed forwarding and address uses within |
| 135 | +/// the current scope. Assumes inner scopes *are* linear. |
| 136 | +/// |
| 137 | +/// - Interior copy-extension is used to canonicalize an OSSA lifetime |
| 138 | +/// |
| 139 | +/// - This header provides no interface for extended interior liveness because |
| 140 | +/// it is highly specific to a particular task. |
| 141 | +/// |
| 142 | +//===----------------------------------------------------------------------===// |
| 143 | + |
| 144 | +#ifndef SWIFT_SIL_OWNERSHIPLIVENESS_H |
| 145 | +#define SWIFT_SIL_OWNERSHIPLIVENESS_H |
| 146 | + |
| 147 | +#include "swift/Basic/Debug.h" |
| 148 | +#include "swift/Basic/LLVM.h" |
| 149 | +#include "swift/SIL/PrunedLiveness.h" |
| 150 | +#include "swift/SIL/OwnershipUseVisitor.h" |
| 151 | +#include "swift/SIL/SILArgument.h" |
| 152 | +#include "swift/SIL/SILBasicBlock.h" |
| 153 | +#include "swift/SIL/SILInstruction.h" |
| 154 | +#include "swift/SIL/SILValue.h" |
| 155 | +#include "llvm/ADT/SmallVector.h" |
| 156 | + |
| 157 | +namespace swift { |
| 158 | + |
| 159 | +/// Analyze SSA liveness of values that introduce an OSSA live range: |
| 160 | +/// |
| 161 | +/// 1. Owned non-phi values |
| 162 | +/// 2. Owned phi values |
| 163 | +/// 3. Borrow scope introducers: begin_borrow/load_borrow |
| 164 | +/// 4. Reborrows: guaranteed phis that end their operands' borrow scopes and |
| 165 | +/// require their own post-dominating end_borrows. |
| 166 | +/// |
| 167 | +/// Used for OSSA lifetime completion. |
| 168 | +class OSSALiveness { |
| 169 | +protected: |
| 170 | + SILValue ownershipDef; |
| 171 | + |
| 172 | + // MARK: Results. |
| 173 | + |
| 174 | + SmallVector<SILBasicBlock *, 8> discoveredBlocks; |
| 175 | + SSAPrunedLiveness liveness; |
| 176 | + |
| 177 | + OSSALiveness(const OSSALiveness &) = delete; |
| 178 | + OSSALiveness &operator=(const OSSALiveness &) = delete; |
| 179 | + |
| 180 | +public: |
| 181 | + OSSALiveness(SILValue def): ownershipDef(def), liveness(&discoveredBlocks) {} |
| 182 | + |
| 183 | + const SSAPrunedLiveness &getLiveness() const { return liveness; } |
| 184 | + |
| 185 | + ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const { |
| 186 | + return discoveredBlocks; |
| 187 | + } |
| 188 | + |
| 189 | + void print(llvm::raw_ostream &OS) const; |
| 190 | + void dump() const; |
| 191 | +}; |
| 192 | + |
| 193 | +// Internal implementation |
| 194 | +struct LinearLivenessVisitor; |
| 195 | + |
| 196 | +/// Compute ownershipDef's lifetime based on it's lifetime-ending uses, assuming |
| 197 | +/// it is already complete/linear. ownershipDef must be either an owned value or |
| 198 | +/// a local borrow scope introduced (begin_borrow, load_borrow, or |
| 199 | +/// store_borrow). |
| 200 | +/// |
| 201 | +/// This is the simplest OSSA liveness analysis, but is not appropriate for |
| 202 | +/// fixing OSSA lifetimes after a transformation and cannot tell you whether |
| 203 | +/// pointer escapes occur. |
| 204 | +class LinearLiveness : public OSSALiveness { |
| 205 | + friend LinearLivenessVisitor; |
| 206 | + |
| 207 | +public: |
| 208 | + LinearLiveness(SILValue def); |
| 209 | + |
| 210 | + void compute(); |
| 211 | +}; |
| 212 | + |
| 213 | +// Internal implementation |
| 214 | +struct InteriorLivenessVisitor; |
| 215 | + |
| 216 | +/// Compute ownershipDef's lifetime based on all its uses (except those |
| 217 | +/// already enclosed by inner scopes). Returns AddressUseKind::PointerEscape |
| 218 | +/// if a pointer to ownershipDef escapes (and is not already enclosed by an |
| 219 | +/// inner scope). |
| 220 | +class InteriorLiveness : public OSSALiveness { |
| 221 | + friend InteriorLivenessVisitor; |
| 222 | + |
| 223 | + // Handle inner scopes. Called for inner reborrows, inner adjacent reborrows, |
| 224 | + // and address scopes. |
| 225 | + // |
| 226 | + // This may add uses to the inner scope, but it may not modify a use-list |
| 227 | + // in any outer scopes. |
| 228 | + using InnerScopeHandlerRef = llvm::function_ref<void(SILValue)>; |
| 229 | + |
| 230 | +public: |
| 231 | + // Summarize address uses |
| 232 | + AddressUseKind addressUseKind = AddressUseKind::Unknown; |
| 233 | + |
| 234 | + // Record any guaranteed phi uses that are not already enclosed by an outer |
| 235 | + // adjacent phi. |
| 236 | + SmallVector<SILValue, 8> unenclosedPhis; |
| 237 | + |
| 238 | +public: |
| 239 | + InteriorLiveness(SILValue def): OSSALiveness(def) {} |
| 240 | + |
| 241 | + void compute(const DominanceInfo *domInfo, |
| 242 | + InnerScopeHandlerRef handleInnerScope = InnerScopeHandlerRef()); |
| 243 | + |
| 244 | + AddressUseKind getAddressUseKind() const { return addressUseKind; } |
| 245 | + |
| 246 | + ArrayRef<SILValue> getUnenclosedPhis() const { return unenclosedPhis; } |
| 247 | + |
| 248 | + void print(llvm::raw_ostream &OS) const; |
| 249 | + void dump() const; |
| 250 | +}; |
| 251 | + |
| 252 | +// Internal implementation |
| 253 | +struct ExtendedLinearLivenessVisitor; |
| 254 | + |
| 255 | +/// Analyze liveness of values that introduce an OSSA live range. This computes |
| 256 | +/// the phi-extended live range for these four categories of live range |
| 257 | +/// introducing values: |
| 258 | +/// |
| 259 | +/// 1. Owned non-phi values |
| 260 | +/// 2. Owned phi values |
| 261 | +/// 3. Borrow scope introducers: begin_borrow/load_borrow |
| 262 | +/// 4. Reborrows: guaranteed phis that end their incoming borrow scopes and |
| 263 | +/// begin a new borrow scope |
| 264 | +class ExtendedLinearLiveness { |
| 265 | + friend ExtendedLinearLivenessVisitor; |
| 266 | + |
| 267 | + SILValue ownershipDef; |
| 268 | + |
| 269 | + // MARK: Results. |
| 270 | + |
| 271 | + // Because of reborrows, the ssa def may not dominate all |
| 272 | + // uses. Consider the reborrows to be separate defs. |
| 273 | + SmallVector<SILBasicBlock *, 8> discoveredBlocks; |
| 274 | + MultiDefPrunedLiveness liveness; |
| 275 | + |
| 276 | + ExtendedLinearLiveness(const ExtendedLinearLiveness &) = delete; |
| 277 | + ExtendedLinearLiveness &operator=(const ExtendedLinearLiveness &) = delete; |
| 278 | + |
| 279 | +public: |
| 280 | + ExtendedLinearLiveness(SILValue def); |
| 281 | + |
| 282 | + void compute(); |
| 283 | + |
| 284 | + /// The array of defs. The first element is ownershipDef. The remaining |
| 285 | + /// elements are outer reborrows discovered during computation. |
| 286 | + /// |
| 287 | + /// TODO: These are always SILValues. Convert the iterator. |
| 288 | + NodeSetVector::iterator defBegin() const { return liveness.defBegin(); } |
| 289 | + NodeSetVector::iterator defEnd() const { return liveness.defBegin(); } |
| 290 | + |
| 291 | + const MultiDefPrunedLiveness &getLiveness() const { return liveness; } |
| 292 | + |
| 293 | + void print(llvm::raw_ostream &OS) const; |
| 294 | + void dump() const; |
| 295 | +}; |
| 296 | + |
| 297 | +} // namespace swift |
| 298 | + |
| 299 | +#endif |
0 commit comments