@@ -75,6 +75,66 @@ inline bool isForwardingConsume(SILValue value) {
7575 return canOpcodeForwardOwnedValues (value);
7676}
7777
78+ // / Find all "use points" of a guaranteed value within its enclosing borrow
79+ // / scope (without looking through reborrows). To find the use points of the
80+ // / extended borrow scope, after looking through reborrows, use
81+ // / findExtendedTransitiveGuaranteedUses() instead.
82+ // /
83+ // / Accumulate results in \p usePoints. This avoids the need for separate
84+ // / worklist and result vectors. Existing vector elements are ignored.
85+ // /
86+ // / "Use points" are the relevant points for determining lifetime. They are
87+ // / determined differently depending on each of these two cases:
88+ // /
89+ // / 1. If \p guaranteedValue introduces a borrow scope (begin_borrow,
90+ // / load_borrow, or phi), then its only use points are the scope-ending uses,
91+ // / and this function returns true. This is, in fact, equivalent to calling
92+ // / BorrowedValue::visitLocalScopeEndingUses(). Any scope-ending uses that are
93+ // / reborrows are recorded as use points without following the reborrowed
94+ // / uses. The \p visitReborrow callback can be used to transitively process
95+ // / reborrows to discover the extended lifetime. Reborrows may be recursive, so
96+ // / this will require checking membership in a working set. Nested borrow scope
97+ // / are irrelevant to the parent scope's lifetime. They are not considered use
98+ // / points, and reborrows within those nested scope are not visited by \p
99+ // / visitReborrow.
100+ // /
101+ // / 2. If \p guaranteedValue does not introduce a borrow scope (it is not a
102+ // / valid BorrowedValue), then its uses are discovered transitively by looking
103+ // / through forwarding operations. If any use is a PointerEscape, then this
104+ // / returns false without adding more uses--the guaranteed values lifetime is
105+ // / indeterminite. If a use introduces a nested borrow scope, it creates use
106+ // / points where the "extended" borrow scope ends. An extended borrow
107+ // / scope is found by looking through any reborrows that end the nested
108+ // / scope. Other uses within nested borrow scopes are ignored.
109+ bool findTransitiveGuaranteedUses (SILValue guaranteedValue,
110+ SmallVectorImpl<Operand *> &usePoints,
111+ function_ref<void (Operand *)> visitReborrow);
112+
113+ // / Find all "use points" of guaranteed value across its extended borrow scope
114+ // / (looking through reborrows). The "use points" are the relevant points for
115+ // / determining lifetime.
116+ // /
117+ // / Accumulate results in \p usePoints. This avoids the need for separate
118+ // / worklist and result vectors. Existing vector elements are ignored.
119+ // /
120+ // / "Use points" are the relevant points for determining lifetime. They are
121+ // / determined differently depending on each of these two cases:
122+ // /
123+ // / 1. If \p guaranteedValue introduces a borrow scope (begin_borrow,
124+ // / load_borrow, or phi), then its only use points are the extended scope-ending
125+ // / uses, and this function returns true. This is, in fact, equivalent to
126+ // / calling BorrowedValue::visitExtendedLocalScopeEndingUses().
127+ // /
128+ // / 2. If \p guaranteedValue does not introduce a borrow scope (it is not a
129+ // / valid BorrowedValue), then its uses are discovered transitively by looking
130+ // / through forwarding operations. Only a BorrowedValue can have its lifetime
131+ // / extended by a reborrow; therefore, in this case, the algorithm is equivalent
132+ // / to findTransitiveGuaranteedUses(). See those comments for more detail.
133+ bool findExtendedTransitiveGuaranteedUses (
134+ SILValue guaranteedValue,
135+ SmallVectorImpl<Operand *> &usePoints);
136+
137+ // / An operand that forwards ownership to one or more results.
78138class ForwardingOperand {
79139 Operand *use = nullptr ;
80140
@@ -216,16 +276,21 @@ struct BorrowingOperand {
216276 // / over a region of code instead of just for a single instruction, visit
217277 // / those uses.
218278 // /
219- // / Returns true if all visitor invocations returns true. Exits early if a
220- // / visitor returns false.
279+ // / Returns false and early exits if the visitor \p func returns false.
280+ // /
281+ // / For an instantaneous borrow, such as apply, this visits no uses. For
282+ // / begin_apply it visits the end_apply uses. For borrow introducers, it
283+ // / visits the end of the introduced borrow scope.
284+ bool visitScopeEndingUses (function_ref<bool (Operand *)> func) const ;
285+
286+ // / Visit the scope ending operands of the extended scope, after transitively
287+ // / searching through reborrows. These uses might not be dominated by this
288+ // / BorrowingOperand.
221289 // /
222- // / Example: An apply performs an instantaneous recursive borrow of a
223- // / guaranteed value but a begin_apply borrows the value over the entire
224- // / region of code corresponding to the coroutine.
290+ // / Returns false and early exits if the visitor \p func returns false.
225291 // /
226- // / NOTE: Return false from func to stop iterating. Returns false if the
227- // / closure requested to stop early.
228- bool visitLocalEndScopeUses (function_ref<bool (Operand *)> func) const ;
292+ // / Note: this does not visit the intermediate reborrows.
293+ bool visitExtendedScopeEndingUses (function_ref<bool (Operand *)> func) const ;
229294
230295 // / Returns true if this borrow scope operand consumes guaranteed
231296 // / values and produces a new scope afterwards.
@@ -247,10 +312,12 @@ struct BorrowingOperand {
247312 llvm_unreachable (" Covered switch isn't covered?!" );
248313 }
249314
250- // / Is the result of this instruction also a borrow introducer?
315+ // / Return true if the user instruction introduces a borrow scope? This is
316+ // / true for both reborrows and nested borrows.
251317 // /
252- // / TODO: This needs a better name.
253- bool areAnyUserResultsBorrowIntroducers () const {
318+ // / If true, the visitBorrowIntroducingUserResults() can be called to acquire
319+ // / each BorrowedValue that introduces a new borrow scopes.
320+ bool hasBorrowIntroducingUser () const {
254321 // TODO: Can we derive this by running a borrow introducer check ourselves?
255322 switch (kind) {
256323 case BorrowingOperandKind::Invalid:
@@ -267,24 +334,19 @@ struct BorrowingOperand {
267334 llvm_unreachable (" Covered switch isn't covered?!" );
268335 }
269336
270- // / Visit all of the results of the operand's user instruction that are
271- // / consuming uses.
272- void visitUserResultConsumingUses (function_ref<void (Operand *)> visitor) const ;
273-
274337 // / Visit all of the "results" of the user of this operand that are borrow
275338 // / scope introducers for the specific scope that this borrow scope operand
276339 // / summarizes.
277- void
278- visitBorrowIntroducingUserResults (function_ref<void (BorrowedValue)> visitor) const ;
279-
280- // / Passes to visitor all of the consuming uses of this use's using
281- // / instruction.
282340 // /
283- // / This enables one to walk the def-use chain of guaranteed phis for a single
284- // / guaranteed scope by using a worklist and checking if any of the operands
285- // / are BorrowScopeOperands.
286- void visitConsumingUsesOfBorrowIntroducingUserResults (
287- function_ref<void (Operand *)> visitor) const ;
341+ // / Precondition: hasBorrowIntroducingUser() is true
342+ // /
343+ // / Returns false and early exits if \p visitor returns false.
344+ bool visitBorrowIntroducingUserResults (
345+ function_ref<bool (BorrowedValue)> visitor) const ;
346+
347+ // / If this operand's user has a single borrowed value result return a
348+ // / valid BorrowedValue instance.
349+ BorrowedValue getBorrowIntroducingUserResult ();
288350
289351 // / Compute the implicit uses that this borrowing operand "injects" into the
290352 // / set of its operands uses.
@@ -398,21 +460,24 @@ struct InteriorPointerOperand;
398460// / guaranteed results are borrow introducers. In practice this means that
399461// / borrow introducers can not have guaranteed results that are not creating a
400462// / new borrow scope. No such instructions exist today.
463+ // /
464+ // / This provides utilities for visiting the end of the borrow scope introduced
465+ // / by this value. The scope ending uses are always dominated by this value and
466+ // / jointly post-dominate this value (see visitLocalScopeEndingUses()). The
467+ // / extended scope, including reborrows has end points that are not dominated by
468+ // / this value but still jointly post-dominate (see
469+ // / visitExtendedLocalScopeEndingUses()).
401470struct BorrowedValue {
402471 SILValue value;
403- BorrowedValueKind kind;
472+ BorrowedValueKind kind = BorrowedValueKind::Invalid ;
404473
405- BorrowedValue () : value(), kind(BorrowedValueKind::Invalid) {}
474+ BorrowedValue () = default ;
406475
407- // / If value is a borrow introducer return it after doing some checks.
408- // /
409- // / This is the only way to construct a BorrowScopeIntroducingValue. We make
410- // / the primary constructor private for this reason.
411- static BorrowedValue get (SILValue value) {
412- auto kind = BorrowedValueKind::get (value);
413- if (!kind)
414- return {nullptr , kind};
415- return {value, kind};
476+ // / If value is a borrow introducer, create a valid BorrowedValue.
477+ explicit BorrowedValue (SILValue value) {
478+ kind = BorrowedValueKind::get (value);
479+ if (kind)
480+ this ->value = value;
416481 }
417482
418483 operator bool () const { return kind != BorrowedValueKind::Invalid && value; }
@@ -430,14 +495,16 @@ struct BorrowedValue {
430495 // / instructions and pass them individually to visitor. Asserts if this is
431496 // / called with a scope that is not local.
432497 // /
498+ // / Returns false and early exist if \p visitor returns false.
499+ // /
433500 // / The intention is that this method can be used instead of
434501 // / BorrowScopeIntroducingValue::getLocalScopeEndingUses() to avoid
435502 // / introducing an intermediate array when one needs to transform the
436503 // / instructions before storing them.
437504 // /
438505 // / NOTE: To determine if a scope is a local scope, call
439506 // / BorrowScopeIntoducingValue::isLocalScope().
440- void visitLocalScopeEndingUses (function_ref<void (Operand *)> visitor) const ;
507+ bool visitLocalScopeEndingUses (function_ref<bool (Operand *)> visitor) const ;
441508
442509 bool isLocalScope () const { return kind.isLocalScope (); }
443510
@@ -451,9 +518,10 @@ struct BorrowedValue {
451518 DeadEndBlocks &deadEndBlocks) const ;
452519
453520 // / Given a local borrow scope introducer, visit all non-forwarding consuming
454- // / users. This means that this looks through guaranteed block arguments.
455- bool visitLocalScopeTransitiveEndingUses (
456- function_ref<void (Operand *)> visitor) const ;
521+ // / users. This means that this looks through guaranteed block arguments. \p
522+ // / visitor is *not* called on Reborrows, only on final scope ending uses.
523+ bool visitExtendedLocalScopeEndingUses (
524+ function_ref<bool (Operand *)> visitor) const ;
457525
458526 void print (llvm::raw_ostream &os) const ;
459527 SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
@@ -492,12 +560,6 @@ struct BorrowedValue {
492560 SILValue operator ->() const { return value; }
493561 SILValue operator *() { return value; }
494562 SILValue operator *() const { return value; }
495-
496- private:
497- // / Internal constructor for failable static constructor. Please do not expand
498- // / its usage since it assumes the code passed in is well formed.
499- BorrowedValue (SILValue value, BorrowedValueKind kind)
500- : value(value), kind(kind) {}
501563};
502564
503565llvm::raw_ostream &operator <<(llvm::raw_ostream &os,
@@ -632,14 +694,15 @@ struct InteriorPointerOperand {
632694 // / Return the end scope of all borrow introducers of the parent value of this
633695 // / projection. Returns true if we were able to find all borrow introducing
634696 // / values.
635- bool visitBaseValueScopeEndingUses (function_ref<void (Operand *)> func) const {
697+ bool visitBaseValueScopeEndingUses (function_ref<bool (Operand *)> func) const {
636698 SmallVector<BorrowedValue, 4 > introducers;
637699 if (!getAllBorrowIntroducingValues (operand->get (), introducers))
638700 return false ;
639701 for (const auto &introducer : introducers) {
640702 if (!introducer.isLocalScope ())
641703 continue ;
642- introducer.visitLocalScopeEndingUses (func);
704+ if (!introducer.visitLocalScopeEndingUses (func))
705+ return false ;
643706 }
644707 return true ;
645708 }
0 commit comments