Skip to content

Commit 33eedc1

Browse files
authored
Merge pull request #67874 from gottesmm/pr-179e2ec3ed29bb0d8c35dc99f710a18571a0a24e
[sil] Convert AddressWalker from using virtual methods to use CRTP and add a transitive -> endpoint user API
2 parents fbfeb4f + 273c357 commit 33eedc1

File tree

8 files changed

+247
-256
lines changed

8 files changed

+247
-256
lines changed

include/swift/SIL/AddressWalker.h

Lines changed: 223 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
#ifndef SWIFT_SIL_ADDRESSWALKER_H
2222
#define SWIFT_SIL_ADDRESSWALKER_H
2323

24+
#include "swift/Basic/Defer.h"
2425
#include "swift/SIL/AddressUseKind.h"
26+
#include "swift/SIL/BasicBlockDatastructures.h"
27+
#include "swift/SIL/InstructionUtils.h"
28+
#include "swift/SIL/Projection.h"
2529
#include "swift/SIL/SILValue.h"
2630

2731
namespace swift {
@@ -33,15 +37,16 @@ namespace swift {
3337
/// Validated by the SIL verifier as always being able to visit all addresses
3438
/// derived from alloc_stack, ref_element_addr, project_box, ref_tail_addr and
3539
/// all other address roots.
40+
template <typename Impl>
3641
class TransitiveAddressWalker {
3742
/// Whether we could tell if this address use didn't escape, did have a
3843
/// pointer escape, or unknown if we failed to understand something.
3944
AddressUseKind result = AddressUseKind::NonEscaping;
4045

4146
unsigned didInvalidate = false;
4247

43-
public:
44-
virtual ~TransitiveAddressWalker() {}
48+
Impl &asImpl() { return *reinterpret_cast<Impl *>(this); }
49+
const Impl &asImpl() const { return *reinterpret_cast<const Impl *>(this); }
4550

4651
protected:
4752
/// Customization point for visiting uses. Returns true if we should continue
@@ -50,9 +55,17 @@ class TransitiveAddressWalker {
5055
/// NOTE: Do not call this directly from within
5156
/// findTransitiveUsesForAddress. Please call callVisitUse. This is intended
5257
/// just for subclasses to override.
53-
virtual bool visitUse(Operand *use) { return true; }
58+
bool visitUse(Operand *use) { return true; }
59+
60+
/// Customization point for visiting operands that we were unable to
61+
/// understand. These cause us to return AddressUseKind::Unknown.
62+
void onError(Operand *use) {}
5463

55-
virtual void onError(Operand *use) {}
64+
/// Customization point that causes the walker to treat a specific transitive
65+
/// use as an end point use.
66+
///
67+
/// Example: Visiting a mutable or immutable open_existential_addr.
68+
bool visitTransitiveUseAsEndPointUse(Operand *use) { return false; }
5669

5770
void meet(AddressUseKind other) {
5871
assert(!didInvalidate);
@@ -63,21 +76,219 @@ class TransitiveAddressWalker {
6376
/// Shim that actually calls visitUse and changes early exit.
6477
void callVisitUse(Operand *use) {
6578
assert(!didInvalidate);
66-
if (!visitUse(use))
79+
if (!asImpl().visitUse(use))
6780
result = AddressUseKind::Unknown;
6881
}
6982

7083
public:
7184
AddressUseKind walk(SILValue address) &&;
7285
};
7386

74-
/// The algorithm that is used to determine what the verifier will consider to
75-
/// be transitive uses of the given address. Used to implement \see
76-
/// findTransitiveUses.
77-
///
78-
/// Returns \p AccessUseKind::Unknown on error.
79-
AddressUseKind findTransitiveUsesForAddress(SILValue address,
80-
TransitiveAddressWalker &visitor);
87+
template <typename Impl>
88+
inline AddressUseKind
89+
TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
90+
assert(!didInvalidate);
91+
92+
// When we exit, set the result to be invalidated so we can't use this again.
93+
SWIFT_DEFER { didInvalidate = true; };
94+
95+
StackList<Operand *> worklist(projectedAddress->getFunction());
96+
SmallPtrSet<Operand *, 32> visitedOperands;
97+
98+
auto addToWorklist = [&](Operand *use) {
99+
if (visitedOperands.insert(use).second)
100+
worklist.push_back(use);
101+
};
102+
103+
for (auto *use : projectedAddress->getUses()) {
104+
addToWorklist(use);
105+
}
106+
107+
// Record all uses that aren't transitively followed. These are either
108+
// instanteneous uses of the addres, or cause a pointer escape.
109+
auto transitiveResultUses = [&](Operand *use) {
110+
auto *svi = cast<SingleValueInstruction>(use->getUser());
111+
if (svi->use_empty()) {
112+
return callVisitUse(use);
113+
}
114+
115+
if (asImpl().visitTransitiveUseAsEndPointUse(use))
116+
return callVisitUse(use);
117+
118+
for (auto *use : svi->getUses())
119+
addToWorklist(use);
120+
};
121+
122+
while (!worklist.empty()) {
123+
if (result == AddressUseKind::Unknown)
124+
return AddressUseKind::Unknown;
125+
126+
auto *op = worklist.pop_back_val();
127+
128+
// Skip type dependent operands.
129+
if (op->isTypeDependent())
130+
continue;
131+
132+
// Then update the worklist with new things to find if we recognize this
133+
// inst and then continue. If we fail, we emit an error at the bottom of the
134+
// loop that we didn't recognize the user.
135+
auto *user = op->getUser();
136+
137+
if (auto *ti = dyn_cast<TermInst>(user)) {
138+
switch (ti->getTermKind()) {
139+
case TermKind::BranchInst:
140+
case TermKind::CondBranchInst:
141+
// We could have an address phi. To be conservative, just treat this as
142+
// a point escape.
143+
meet(AddressUseKind::PointerEscape);
144+
for (auto succBlockArgList : ti->getSuccessorBlockArgumentLists()) {
145+
auto *succ = succBlockArgList[op->getOperandNumber()];
146+
for (auto *use : succ->getUses())
147+
addToWorklist(use);
148+
}
149+
continue;
150+
151+
case TermKind::UnreachableInst:
152+
case TermKind::UnwindInst:
153+
llvm_unreachable("Should never be used");
154+
case TermKind::SwitchEnumInst:
155+
case TermKind::SwitchValueInst:
156+
case TermKind::DynamicMethodBranchInst:
157+
case TermKind::AwaitAsyncContinuationInst:
158+
case TermKind::CheckedCastBranchInst:
159+
llvm_unreachable("Never takes an address");
160+
// Point uses.
161+
case TermKind::ReturnInst:
162+
case TermKind::ThrowInst:
163+
case TermKind::YieldInst:
164+
case TermKind::TryApplyInst:
165+
case TermKind::SwitchEnumAddrInst:
166+
case TermKind::CheckedCastAddrBranchInst:
167+
callVisitUse(op);
168+
continue;
169+
}
170+
}
171+
172+
// TODO: Partial apply should be NonEscaping, but then we need to consider
173+
// the apply to be a use point.
174+
if (isa<PartialApplyInst>(user) || isa<AddressToPointerInst>(user)) {
175+
meet(AddressUseKind::PointerEscape);
176+
callVisitUse(op);
177+
continue;
178+
}
179+
180+
// First, eliminate "end point uses" that we just need to check liveness at
181+
// and do not need to check transitive uses of.
182+
if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) ||
183+
isa<MarkUnresolvedMoveAddrInst>(user) || isIncidentalUse(user) ||
184+
isa<StoreInst>(user) || isa<DestroyAddrInst>(user) ||
185+
isa<AssignInst>(user) || isa<LoadUnownedInst>(user) ||
186+
isa<StoreUnownedInst>(user) || isa<EndApplyInst>(user) ||
187+
isa<LoadWeakInst>(user) || isa<StoreWeakInst>(user) ||
188+
isa<AssignByWrapperInst>(user) || isa<AssignOrInitInst>(user) ||
189+
isa<BeginUnpairedAccessInst>(user) ||
190+
isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user) ||
191+
isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user) ||
192+
isa<IsUniqueInst>(user) || isa<ValueMetatypeInst>(user) ||
193+
isa<DebugValueInst>(user) || isa<EndBorrowInst>(user) ||
194+
isa<ExplicitCopyAddrInst>(user) || isa<DeallocStackInst>(user) ||
195+
isa<InitBlockStorageHeaderInst>(user) ||
196+
isa<GetAsyncContinuationAddrInst>(user) ||
197+
isa<ExistentialMetatypeInst>(user) ||
198+
isa<UncheckedRefCastAddrInst>(user) || isa<KeyPathInst>(user) ||
199+
isa<RetainValueAddrInst>(user) || isa<ReleaseValueAddrInst>(user) ||
200+
isa<PackElementSetInst>(user) || isa<PackElementGetInst>(user) ||
201+
isa<DeinitExistentialAddrInst>(user) || isa<LoadBorrowInst>(user)) {
202+
callVisitUse(op);
203+
continue;
204+
}
205+
206+
if (isa<UnconditionalCheckedCastAddrInst>(user) ||
207+
isa<MarkFunctionEscapeInst>(user)) {
208+
assert(!user->hasResults());
209+
callVisitUse(op);
210+
continue;
211+
}
212+
213+
// Then handle users that we need to look at transitive uses of.
214+
if (Projection::isAddressProjection(user) ||
215+
isa<ProjectBlockStorageInst>(user) ||
216+
isa<OpenExistentialAddrInst>(user) ||
217+
isa<InitExistentialAddrInst>(user) || isa<InitEnumDataAddrInst>(user) ||
218+
isa<BeginAccessInst>(user) || isa<TailAddrInst>(user) ||
219+
isa<IndexAddrInst>(user) || isa<StoreBorrowInst>(user) ||
220+
isa<UncheckedAddrCastInst>(user) || isa<MarkMustCheckInst>(user) ||
221+
isa<MarkUninitializedInst>(user) || isa<DropDeinitInst>(user) ||
222+
isa<ProjectBlockStorageInst>(user) || isa<UpcastInst>(user) ||
223+
isa<TuplePackElementAddrInst>(user) ||
224+
isa<CopyableToMoveOnlyWrapperAddrInst>(user) ||
225+
isa<MoveOnlyWrapperToCopyableAddrInst>(user)) {
226+
transitiveResultUses(op);
227+
continue;
228+
}
229+
230+
if (auto *builtin = dyn_cast<BuiltinInst>(user)) {
231+
if (auto kind = builtin->getBuiltinKind()) {
232+
switch (*kind) {
233+
case BuiltinValueKind::TSanInoutAccess:
234+
case BuiltinValueKind::ResumeThrowingContinuationReturning:
235+
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
236+
case BuiltinValueKind::Copy:
237+
case BuiltinValueKind::GenericAdd:
238+
case BuiltinValueKind::GenericFAdd:
239+
case BuiltinValueKind::GenericAnd:
240+
case BuiltinValueKind::GenericAShr:
241+
case BuiltinValueKind::GenericLShr:
242+
case BuiltinValueKind::GenericOr:
243+
case BuiltinValueKind::GenericFDiv:
244+
case BuiltinValueKind::GenericMul:
245+
case BuiltinValueKind::GenericFMul:
246+
case BuiltinValueKind::GenericSDiv:
247+
case BuiltinValueKind::GenericExactSDiv:
248+
case BuiltinValueKind::GenericShl:
249+
case BuiltinValueKind::GenericSRem:
250+
case BuiltinValueKind::GenericSub:
251+
case BuiltinValueKind::GenericFSub:
252+
case BuiltinValueKind::GenericUDiv:
253+
case BuiltinValueKind::GenericExactUDiv:
254+
case BuiltinValueKind::GenericURem:
255+
case BuiltinValueKind::GenericFRem:
256+
case BuiltinValueKind::GenericXor:
257+
case BuiltinValueKind::TaskRunInline:
258+
case BuiltinValueKind::ZeroInitializer:
259+
callVisitUse(op);
260+
continue;
261+
default:
262+
break;
263+
}
264+
}
265+
}
266+
267+
if (auto fas = FullApplySite::isa(user)) {
268+
callVisitUse(op);
269+
continue;
270+
}
271+
272+
if (auto *mdi = dyn_cast<MarkDependenceInst>(user)) {
273+
// If this is the base, just treat it as a liveness use.
274+
if (op->get() == mdi->getBase()) {
275+
callVisitUse(op);
276+
continue;
277+
}
278+
279+
// If we are the value use, look through it.
280+
transitiveResultUses(op);
281+
continue;
282+
}
283+
284+
// We were unable to recognize this user, so set AddressUseKind to unknown
285+
// and call onError with the specific user that caused the problem.
286+
asImpl().onError(op);
287+
return AddressUseKind::Unknown;
288+
}
289+
290+
return result;
291+
}
81292

82293
} // namespace swift
83294

include/swift/SIL/OwnershipUtils.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -736,15 +736,16 @@ inline AddressUseKind findTransitiveUsesForAddress(
736736
// guaranteed uses to determine if a load_borrow is an escape in OSSA. This
737737
// is OSSA specific behavior and we should probably create a different API
738738
// for that. But for now, this lets this APIs users stay the same.
739-
struct BasicTransitiveAddressVisitor final : TransitiveAddressWalker {
739+
struct BasicTransitiveAddressVisitor
740+
: TransitiveAddressWalker<BasicTransitiveAddressVisitor> {
740741
SmallVectorImpl<Operand *> *foundUses;
741742
std::function<void(Operand *)> *onErrorFunc;
742743

743744
BasicTransitiveAddressVisitor(SmallVectorImpl<Operand *> *foundUses,
744745
std::function<void(Operand *)> *onErrorFunc)
745746
: foundUses(foundUses), onErrorFunc(onErrorFunc) {}
746747

747-
bool visitUse(Operand *use) override {
748+
bool visitUse(Operand *use) {
748749
if (!foundUses)
749750
return true;
750751

@@ -771,7 +772,7 @@ inline AddressUseKind findTransitiveUsesForAddress(
771772
return true;
772773
}
773774

774-
void onError(Operand *use) override {
775+
void onError(Operand *use) {
775776
if (onErrorFunc)
776777
(*onErrorFunc)(use);
777778
}

0 commit comments

Comments
 (0)