@@ -353,6 +353,61 @@ struct LiveRangeSummary {
353353// / necessarily include liveness up to destroy_value or end_borrow
354354// / instructions.
355355class PrunedLiveness {
356+ public:
357+ // / A tristate describing how an instruction uses the value:
358+ // / - non-ending: the value's lifetime does not end at the instruction;
359+ // / results from updateForUse(, lifetimeEnding: false)
360+ // / - ending: the value's lifetime ends at the instruction; results from
361+ // / updateForUse(, lifetimeEnding: true)
362+ // / - non-use: the instruction doesn't use the value but liveness was extended
363+ // / to it; results from extendToNonUse
364+ // /
365+ // / If an instruction is added to liveness with multiple LifetimeEnding
366+ // / instances, the stored instance needs to be updated appropriately, taking
367+ // / into account the instances that were already seen.
368+ // /
369+ // / For example, if Nonuse is seen after Ending, it is ignored: that the
370+ // / instruction ends the lifetime takes priority: the instruction doesn't end
371+ // / the lifetime of the value any less because liveness was extended to it.
372+ // /
373+ // / Similarly, if Ending is seen after NonEnding, it is ignored: that the
374+ // / instruction ends the lifetime of a copy of the value can't change the fact
375+ // / that liveness already extends _beyond_ the instruction because it was
376+ // / already recognized as a non-ending use.
377+ // /
378+ // / This relationship of "overriding" is captured by the order of the cases in
379+ // / LifetimeEnding::Value and which case overrides another is computed by
380+ // / taking the meet--i.e. the lesser of the two cases overrides.
381+ // /
382+ // / Note: Taking the meet may not be appropriate for branch instructions
383+ // / which may need to be recognized as lifetime-ending when they have
384+ // / as uses both a reborrow and a guaranteed phi.
385+ struct LifetimeEnding {
386+ enum class Value {
387+ // The instruction doesn't consume the value.
388+ NonEnding,
389+ // The instruction consumes the value.
390+ Ending,
391+ // The instruction doesn't use the value.
392+ NonUse,
393+ };
394+ Value value;
395+
396+ LifetimeEnding (Value value) : value(value) {}
397+ explicit LifetimeEnding (bool lifetimeEnding)
398+ : value(lifetimeEnding ? Value::Ending : Value::NonEnding) {}
399+ operator Value () const { return value; }
400+ LifetimeEnding meet (LifetimeEnding const other) const {
401+ return value < other.value ? *this : other;
402+ }
403+ void meetInPlace (LifetimeEnding const other) { *this = meet (other); }
404+ bool isEnding () const { return value == Value::Ending; }
405+
406+ static LifetimeEnding NonUse () { return {Value::NonUse}; };
407+ static LifetimeEnding Ending () { return {Value::Ending}; };
408+ static LifetimeEnding NonEnding () { return {Value::NonEnding}; };
409+ };
410+
356411protected:
357412 PrunedLiveBlocks liveBlocks;
358413
@@ -365,7 +420,7 @@ class PrunedLiveness {
365420 // they may be the last use in the block.
366421 //
367422 // Non-lifetime-ending within a LiveOut block are uninteresting.
368- llvm::SmallMapVector<SILInstruction *, bool , 8 > users;
423+ llvm::SmallMapVector<SILInstruction *, LifetimeEnding , 8 > users;
369424
370425public:
371426 PrunedLiveness (SILFunction *function,
@@ -411,11 +466,12 @@ class PrunedLiveness {
411466 auto useIter = users.find (user);
412467 if (useIter == users.end ())
413468 return NonUser;
414- return useIter->second ? LifetimeEndingUse : NonLifetimeEndingUse;
469+ return useIter->second == LifetimeEnding::Ending () ? LifetimeEndingUse
470+ : NonLifetimeEndingUse;
415471 }
416472
417473 using ConstUserRange =
418- iterator_range<const std::pair<SILInstruction *, bool > *>;
474+ iterator_range<const std::pair<SILInstruction *, LifetimeEnding > *>;
419475 ConstUserRange getAllUsers () const {
420476 return llvm::make_range (users.begin (), users.end ());
421477 }
@@ -425,49 +481,53 @@ class PrunedLiveness {
425481 // / IDE.
426482 struct RangeIterationHelpers {
427483 struct MapFunctor {
428- SILInstruction *
429- operator ()( const std::pair<SILInstruction *, bool > &pair) const {
484+ SILInstruction *operator ()(
485+ const std::pair<SILInstruction *, LifetimeEnding > &pair) const {
430486 // Strip off the const to ease use with other APIs.
431487 return const_cast <SILInstruction *>(pair.first );
432488 }
433489 };
434490
435- struct LifetimeEnding {
491+ struct IsLifetimeEnding {
436492 struct FilterFunctor {
437- bool operator ()(const std::pair<SILInstruction *, bool > &pair) const {
438- return pair.second ;
493+ bool operator ()(
494+ const std::pair<SILInstruction *, LifetimeEnding> &pair) const {
495+ return pair.second .isEnding ();
439496 }
440497 };
441498
442499 using MapFilterIter = llvm::mapped_iterator<
443- llvm::filter_iterator<const std::pair<SILInstruction *, bool > *,
444- FilterFunctor>,
500+ llvm::filter_iterator<
501+ const std::pair<SILInstruction *, LifetimeEnding> *,
502+ FilterFunctor>,
445503 MapFunctor>;
446504 };
447505
448506 struct NonLifetimeEnding {
449507 struct FilterFunctor {
450- bool operator ()(const std::pair<SILInstruction *, bool > &pair) const {
451- return !pair.second ;
508+ bool operator ()(
509+ const std::pair<SILInstruction *, LifetimeEnding> &pair) const {
510+ return !pair.second .isEnding ();
452511 }
453512 };
454513
455514 using MapFilterIter = llvm::mapped_iterator<
456- llvm::filter_iterator<const std::pair<SILInstruction *, bool > *,
457- FilterFunctor>,
515+ llvm::filter_iterator<
516+ const std::pair<SILInstruction *, LifetimeEnding> *,
517+ FilterFunctor>,
458518 MapFunctor>;
459519 };
460520 };
461521 using LifetimeEndingUserRange = llvm::iterator_range<
462- RangeIterationHelpers::LifetimeEnding ::MapFilterIter>;
522+ RangeIterationHelpers::IsLifetimeEnding ::MapFilterIter>;
463523
464524 // / Return a range consisting of the current set of consuming users fed into
465525 // / this PrunedLiveness instance.
466526 LifetimeEndingUserRange getLifetimeEndingUsers () const {
467527 return map_range (
468528 llvm::make_filter_range (
469529 getAllUsers (),
470- RangeIterationHelpers::LifetimeEnding ::FilterFunctor ()),
530+ RangeIterationHelpers::IsLifetimeEnding ::FilterFunctor ()),
471531 RangeIterationHelpers::MapFunctor ());
472532 }
473533
@@ -552,6 +612,8 @@ class PrunedLiveRange : public PrunedLiveness {
552612 ValueSet &visited,
553613 SILValue value);
554614
615+ void updateForUse (SILInstruction *user, LifetimeEnding lifetimeEnding);
616+
555617public:
556618 // / For flexibility, \p lifetimeEnding is provided by the
557619 // / caller. PrunedLiveness makes no assumptions about the def-use
@@ -560,6 +622,12 @@ class PrunedLiveRange : public PrunedLiveness {
560622 // / live range vs. a nested borrow scope within the extended live range.
561623 void updateForUse (SILInstruction *user, bool lifetimeEnding);
562624
625+ // / Adds \p inst which doesn't use the def to liveness.
626+ // /
627+ // / Different from calling updateForUse because it never overrides the value
628+ // / \p lifetimeEnding stored for \p inst.
629+ void extendToNonUse (SILInstruction *inst);
630+
563631 // / Updates the liveness for a whole borrow scope, beginning at \p op.
564632 // / Returns false if this cannot be done. This assumes that nested OSSA
565633 // / lifetimes are complete.
0 commit comments