Skip to content

Commit 2b8cfca

Browse files
committed
Add OwnershipLiveness utilities
Encapsulate all the complexity of reborrows and guaranteed phi in 3 ownership liveness interfaces: LinerLiveness, InteriorLiveness, and ExtendedLiveness.
1 parent 2d91316 commit 2b8cfca

File tree

7 files changed

+1915
-0
lines changed

7 files changed

+1915
-0
lines changed

include/swift/SIL/OwnershipLiveness.h

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
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+
///
21+
/// Linear SSA liveness:
22+
///
23+
/// - Liveness for a single "defining" owned value or borrow introducing value
24+
/// assuming the value's lifetime is already linear.
25+
///
26+
/// - The definition dominates all use points (phis do not extend the lifetime)
27+
///
28+
/// - Only lifetime-ending operations generate liveness
29+
///
30+
/// - Oblivious to pointer escapes within the lifetime
31+
///
32+
///
33+
/// Interior SSA liveness:
34+
///
35+
/// - Liveness for a single "defining" value with any ownership.
36+
///
37+
/// - The definition dominates all use points (phis do not extend the lifetime)
38+
///
39+
/// - Does not assume the current lifetime is linear. Transitively follows
40+
/// guaranteed forwarding and address uses within the current scope.
41+
///
42+
/// - Liveness cannot extend beyond lifetime-ending operations
43+
/// (a.k.a. affine lifetimes).
44+
///
45+
/// - Assumes inner scopes *are* linear, including borrow and address scopes
46+
/// (e.g. begin_borrow, load_borrow, begin_apply, store_borrow, begin_access)
47+
/// A callback may be used to complete inner scopes before updating liveness.
48+
/// Rarely returns AddressUseKind::PointerEscape, because inner scopes hide
49+
/// pointer escapes.
50+
///
51+
/// - Insulates outer scopes from inner scope details. Maintains the
52+
/// invariant that inlining cannot pessimize optimization.
53+
///
54+
/// - Interior SSA liveness is used to complete (linearize) an OSSA lifetime
55+
///
56+
///
57+
/// Transitive SSA liveness
58+
///
59+
/// - Liveness for a single "defining" value with any ownership.
60+
///
61+
/// - The definition dominates all use points (phis do not extend the lifetime)
62+
///
63+
/// - Does not assume that any lifetimes are linear. Transitively follows uses
64+
/// within inner scopes, recursively through nested scopes, including
65+
/// forwarding operations and address uses.
66+
///
67+
/// - Much more likely to return AddressUseKind::PointerEscape
68+
///
69+
/// - This is no longer needed with complete OSSA lifetimes
70+
///
71+
///
72+
/// Extended liveness
73+
///
74+
/// - Liveness of a single "defining" value extended beyond its lifetime-ending
75+
/// operations.
76+
///
77+
/// - May refer to copy-extension, reborrow-extension, or extension over
78+
/// barriers such as access markers.
79+
///
80+
/// - For reborrow-extension, the definition no longer dominates the use
81+
/// points. MultiDefPrunedLiveness must be used. Each extended reborrow is
82+
/// added as a new definition.
83+
///
84+
///
85+
/// Extended linear liveness
86+
///
87+
/// - Only considers lifetime-ending operations. Assumes the definition already
88+
/// has a linear lifetime and all copy-extended or reborrow-extended lifetimes
89+
/// are also linear.
90+
///
91+
///
92+
/// Extended interior liveness
93+
///
94+
/// - Like interior SSA liveness, does not assume the current lifetime is
95+
/// linear. Transitively follows guaranteed forwarding and address uses within
96+
/// the current scope. Assumes inner scopes *are* linear.
97+
///
98+
/// - Interior copy-extension is used to canonicalize an OSSA lifetime
99+
///
100+
/// - Interior reborrow-extension is used to check borrow scopes relative to
101+
/// their inner uses and outer lifetime.
102+
///
103+
//===----------------------------------------------------------------------===//
104+
///
105+
/// Completing an OSSA lifetime may require phi creation whenever a reborrow is
106+
/// not dominated by the OSSA definition. The new phi's lifetime will then be
107+
/// extended over any of its inner adjacent phis.
108+
///
109+
//===----------------------------------------------------------------------===//
110+
111+
#ifndef SWIFT_SIL_OWNERSHIPLIVENESS_H
112+
#define SWIFT_SIL_OWNERSHIPLIVENESS_H
113+
114+
#include "swift/Basic/Debug.h"
115+
#include "swift/Basic/LLVM.h"
116+
#include "swift/SIL/PrunedLiveness.h"
117+
#include "swift/SIL/OwnershipUseVisitor.h"
118+
#include "swift/SIL/SILArgument.h"
119+
#include "swift/SIL/SILBasicBlock.h"
120+
#include "swift/SIL/SILInstruction.h"
121+
#include "swift/SIL/SILValue.h"
122+
#include "llvm/ADT/SmallVector.h"
123+
124+
namespace swift {
125+
126+
/// Analyze SSA liveness of values that introduce an OSSA live range:
127+
///
128+
/// 1. Owned non-phi values
129+
/// 2. Owned phi values
130+
/// 3. Borrow scope introducers: begin_borrow/load_borrow
131+
/// 4. Reborrows: guaranteed phis transitively defined by at least one borrow
132+
/// scope introducer.
133+
///
134+
/// Used for OSSA lifetime completion.
135+
class OSSALiveness {
136+
protected:
137+
SILValue ownershipDef;
138+
139+
// MARK: Results.
140+
141+
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
142+
SSAPrunedLiveness liveness;
143+
144+
OSSALiveness(const OSSALiveness &) = delete;
145+
OSSALiveness &operator=(const OSSALiveness &) = delete;
146+
147+
public:
148+
OSSALiveness(SILValue def): ownershipDef(def), liveness(&discoveredBlocks) {}
149+
150+
const SSAPrunedLiveness &getLiveness() const { return liveness; }
151+
152+
ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const {
153+
return discoveredBlocks;
154+
}
155+
156+
void print(llvm::raw_ostream &OS) const;
157+
void dump() const;
158+
};
159+
160+
// Internal implementation
161+
struct LinearLivenessVisitor;
162+
163+
/// Compute ownershipDef's lifetime based on it's lifetime-ending uses, assuming
164+
/// it is already complete/linear. ownershipDef must be either an owned value or
165+
/// a local borrow scope introduced (begin_borrow, load_borrow, or
166+
/// store_borrow).
167+
///
168+
/// This is the simplest OSSA liveness analysis, but is not appropriate for
169+
/// fixing OSSA lifetimes after transformation and cannot tell you whether
170+
/// pointer escapes occur.
171+
class LinearLiveness : public OSSALiveness {
172+
friend LinearLivenessVisitor;
173+
174+
public:
175+
LinearLiveness(SILValue def);
176+
177+
void compute();
178+
};
179+
180+
// Internal implementation
181+
struct InteriorLivenessVisitor;
182+
183+
/// Compute ownershipDef's lifetime based on all its uses (except those
184+
/// already enclosed by inner scopes). Returns AddressUseKind::PointerEscape
185+
/// if a pointer to ownershipDef escapes (and is not already enclosed by an
186+
/// inner scope).
187+
class InteriorLiveness : public OSSALiveness {
188+
friend InteriorLivenessVisitor;
189+
190+
// Handle inner scopes. Called for inner reborrows, inner adjacent reborrows,
191+
// and address scopes.
192+
//
193+
// This may add uses to the inner scope, but it may not modify a use-list
194+
// in any outer scopes.
195+
using InnerScopeHandlerRef = llvm::function_ref<void(SILValue)>;
196+
197+
public:
198+
// Summarize address uses
199+
AddressUseKind addressUseKind = AddressUseKind::Unknown;
200+
201+
// Record any guaranteed phi uses that are not already enclosed by an outer
202+
// adjacent phi.
203+
SmallVector<SILValue, 8> unenclosedPhis;
204+
205+
public:
206+
InteriorLiveness(SILValue def): OSSALiveness(def) {}
207+
208+
void compute(const DominanceInfo *domInfo,
209+
InnerScopeHandlerRef handleInnerScope = InnerScopeHandlerRef());
210+
211+
AddressUseKind getAddressUseKind() const { return addressUseKind; }
212+
213+
ArrayRef<SILValue> getUnenclosedPhis() const { return unenclosedPhis; }
214+
215+
void print(llvm::raw_ostream &OS) const;
216+
void dump() const;
217+
};
218+
219+
// Internal implementation
220+
struct ExtendedLivenessVisitor;
221+
222+
/// Analyze liveness of values that introduce an OSSA live range. This computes
223+
/// the phi-extended live range for these four categories of live range
224+
/// introducing values:
225+
///
226+
/// 1. Owned non-phi values
227+
/// 2. Owned phi values
228+
/// 3. Borrow scope introducers: begin_borrow/load_borrow
229+
/// 4. Reborrows: guaranteed phis that end their incoming borrow scopes and
230+
/// begin a new borrow scope
231+
class ExtendedLiveness {
232+
friend ExtendedLivenessVisitor;
233+
234+
SILValue ownershipDef;
235+
236+
// MARK: Results.
237+
238+
// Because of reborrows, the ssa def may not dominate all
239+
// uses. Consider the reborrows to be separate defs.
240+
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
241+
MultiDefPrunedLiveness liveness;
242+
243+
ExtendedLiveness(const ExtendedLiveness &) = delete;
244+
ExtendedLiveness &operator=(const ExtendedLiveness &) = delete;
245+
246+
public:
247+
ExtendedLiveness(SILValue def);
248+
249+
void compute();
250+
251+
/// The array of defs. The first element is ownershipDef. The remaining
252+
/// elements are outer reborrows discovered during computation.
253+
///
254+
/// TODO: These are always SILValues. Convert the iterator.
255+
NodeSetVector::iterator defBegin() const { return liveness.defBegin(); }
256+
NodeSetVector::iterator defEnd() const { return liveness.defBegin(); }
257+
258+
const MultiDefPrunedLiveness &getLiveness() const { return liveness; }
259+
260+
void print(llvm::raw_ostream &OS) const;
261+
void dump() const;
262+
};
263+
264+
} // namespace swift
265+
266+
#endif

0 commit comments

Comments
 (0)