@@ -75,6 +75,66 @@ inline bool isForwardingConsume(SILValue value) {
75
75
return canOpcodeForwardOwnedValues (value);
76
76
}
77
77
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.
78
138
class ForwardingOperand {
79
139
Operand *use = nullptr ;
80
140
@@ -216,16 +276,21 @@ struct BorrowingOperand {
216
276
// / over a region of code instead of just for a single instruction, visit
217
277
// / those uses.
218
278
// /
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.
221
289
// /
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.
225
291
// /
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 ;
229
294
230
295
// / Returns true if this borrow scope operand consumes guaranteed
231
296
// / values and produces a new scope afterwards.
@@ -247,10 +312,12 @@ struct BorrowingOperand {
247
312
llvm_unreachable (" Covered switch isn't covered?!" );
248
313
}
249
314
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.
251
317
// /
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 {
254
321
// TODO: Can we derive this by running a borrow introducer check ourselves?
255
322
switch (kind) {
256
323
case BorrowingOperandKind::Invalid:
@@ -267,24 +334,19 @@ struct BorrowingOperand {
267
334
llvm_unreachable (" Covered switch isn't covered?!" );
268
335
}
269
336
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
-
274
337
// / Visit all of the "results" of the user of this operand that are borrow
275
338
// / scope introducers for the specific scope that this borrow scope operand
276
339
// / 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.
282
340
// /
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 ();
288
350
289
351
// / Compute the implicit uses that this borrowing operand "injects" into the
290
352
// / set of its operands uses.
@@ -398,22 +460,32 @@ struct InteriorPointerOperand;
398
460
// / guaranteed results are borrow introducers. In practice this means that
399
461
// / borrow introducers can not have guaranteed results that are not creating a
400
462
// / 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()).
401
470
struct BorrowedValue {
402
471
SILValue value;
403
- BorrowedValueKind kind;
472
+ BorrowedValueKind kind = BorrowedValueKind::Invalid ;
404
473
405
- BorrowedValue () : value(), kind(BorrowedValueKind::Invalid) {}
474
+ BorrowedValue () = default ;
475
+
476
+ // / If \p value is a borrow introducer construct a valid BorrowedValue.
477
+ BorrowedValue (SILValue value) {
478
+ kind = BorrowedValueKind::get (value);
479
+ if (!kind)
480
+ return ;
481
+ this ->value = value;
482
+ }
406
483
407
484
// / If value is a borrow introducer return it after doing some checks.
408
485
// /
409
486
// / This is the only way to construct a BorrowScopeIntroducingValue. We make
410
487
// / 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};
416
- }
488
+ static BorrowedValue get (SILValue value) { return BorrowedValue (value); }
417
489
418
490
operator bool () const { return kind != BorrowedValueKind::Invalid && value; }
419
491
@@ -430,14 +502,16 @@ struct BorrowedValue {
430
502
// / instructions and pass them individually to visitor. Asserts if this is
431
503
// / called with a scope that is not local.
432
504
// /
505
+ // / Returns false and early exist if \p visitor returns false.
506
+ // /
433
507
// / The intention is that this method can be used instead of
434
508
// / BorrowScopeIntroducingValue::getLocalScopeEndingUses() to avoid
435
509
// / introducing an intermediate array when one needs to transform the
436
510
// / instructions before storing them.
437
511
// /
438
512
// / NOTE: To determine if a scope is a local scope, call
439
513
// / BorrowScopeIntoducingValue::isLocalScope().
440
- void visitLocalScopeEndingUses (function_ref<void (Operand *)> visitor) const ;
514
+ bool visitLocalScopeEndingUses (function_ref<bool (Operand *)> visitor) const ;
441
515
442
516
bool isLocalScope () const { return kind.isLocalScope (); }
443
517
@@ -451,9 +525,10 @@ struct BorrowedValue {
451
525
DeadEndBlocks &deadEndBlocks) const ;
452
526
453
527
// / 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 ;
528
+ // / users. This means that this looks through guaranteed block arguments. \p
529
+ // / visitor is *not* called on Reborrows, only on final scope ending uses.
530
+ bool visitExtendedLocalScopeEndingUses (
531
+ function_ref<bool (Operand *)> visitor) const ;
457
532
458
533
void print (llvm::raw_ostream &os) const ;
459
534
SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
@@ -632,14 +707,15 @@ struct InteriorPointerOperand {
632
707
// / Return the end scope of all borrow introducers of the parent value of this
633
708
// / projection. Returns true if we were able to find all borrow introducing
634
709
// / values.
635
- bool visitBaseValueScopeEndingUses (function_ref<void (Operand *)> func) const {
710
+ bool visitBaseValueScopeEndingUses (function_ref<bool (Operand *)> func) const {
636
711
SmallVector<BorrowedValue, 4 > introducers;
637
712
if (!getAllBorrowIntroducingValues (operand->get (), introducers))
638
713
return false ;
639
714
for (const auto &introducer : introducers) {
640
715
if (!introducer.isLocalScope ())
641
716
continue ;
642
- introducer.visitLocalScopeEndingUses (func);
717
+ if (!introducer.visitLocalScopeEndingUses (func))
718
+ return false ;
643
719
}
644
720
return true ;
645
721
}
0 commit comments