Skip to content

Commit 5523c56

Browse files
authored
Merge pull request #63404 from atrick/ossa-liveness
Add ownership liveness utilities
2 parents 72f9c3f + 13e1aa4 commit 5523c56

File tree

8 files changed

+1948
-3
lines changed

8 files changed

+1948
-3
lines changed

include/swift/SIL/OwnershipLiveness.h

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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

Comments
 (0)