10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
//
13
+ // Utilities that specify ownership SSA (OSSA) lifetimes.
14
+ //
13
15
// TODO: Implement ExtendedLinearLiveness. This requires
14
16
// MultiDefPrunedLiveness, which is not supported by InstructionRange.
15
17
//
@@ -60,28 +62,29 @@ func computeLinearLiveness(for definingValue: Value, _ context: Context)
60
62
return range
61
63
}
62
64
63
- /// Indicate whether OwnershipUseVisitor is visiting a use of the
64
- /// outer OSSA lifetime or within an inner borrow scope (reborrow).
65
- enum IsInnerLifetime {
66
- case outerLifetime
67
- case innerLifetime
68
- }
69
-
70
65
typealias InnerScopeHandler = ( Value ) -> WalkResult
71
66
72
67
/// Compute liveness and return a range, which the caller must deinitialize.
73
68
///
74
- /// An OSSA lifetime begins with a single "defining" value, which
75
- /// must be owned, or must begin a borrow scope.
69
+ /// An OSSA lifetime begins with a single "defining" value, which must
70
+ /// be owned, or must begin a borrow scope. A complete OSSA lifetime
71
+ /// has a linear lifetime, meaning that it has a lifetime-ending use
72
+ /// on all paths. Interior liveness computes liveness without assuming
73
+ /// the lifetime is complete. To do this, it must find all "use
74
+ /// points" and prove that the defining value is never propagated
75
+ /// beyond those points. This is used to initially complete OSSA
76
+ /// lifetimes and fix them after transformations that's don't preserve
77
+ /// OSSA.
76
78
///
77
- /// - Liveness does not extend beyond lifetime-ending operations
78
- /// (a.k.a. affine lifetimes).
79
+ /// Invariants:
79
80
///
80
81
/// - The definition dominates all use points.
81
82
///
82
- /// - Does not assume the current lifetime is complete, but does
83
- /// assume any inner scopes are complete. Use `innerScopeHandler` to
84
- /// complete them or bail-out.
83
+ /// - Liveness does not extend beyond lifetime-ending operations
84
+ /// (a.k.a. affine lifetimes).
85
+ ///
86
+ /// - All inner scopes are complete. (Use `innerScopeHandler` to
87
+ /// complete them or bail-out).
85
88
func computeInteriorLiveness( for definingValue: Value ,
86
89
_ context: FunctionPassContext ,
87
90
innerScopeHandler: InnerScopeHandler ? = nil ) -> InstructionRange {
@@ -115,7 +118,7 @@ func computeInteriorLiveness(for definingValue: Value,
115
118
return range
116
119
}
117
120
118
- /// Visit all interior uses of on OSSA lifetime.
121
+ /// Visit all interior uses of an OSSA lifetime.
119
122
///
120
123
/// - `definingValue` dominates all uses. Only dominated phis extend
121
124
/// the lifetime. All other phis must have a lifetime-ending outer
@@ -132,14 +135,14 @@ func computeInteriorLiveness(for definingValue: Value,
132
135
/// begin_access) A `innerScopeHandler` callback may be used to
133
136
/// complete inner scopes before updating liveness.
134
137
///
135
- /// InteriorUseVisitor can be used to complete (linearize) an OSSA
138
+ /// InteriorUseWalker can be used to complete (linearize) an OSSA
136
139
/// lifetime after transformation that invalidates OSSA.
137
140
///
138
141
/// Example:
139
142
///
140
- /// %struct = struct ...
143
+ /// %s = struct ...
141
144
/// %f = struct_extract %s // defines a guaranteed value (%f)
142
- /// %b = begin_borrow %field
145
+ /// %b = begin_borrow %f
143
146
/// %a = ref_element_addr %b
144
147
/// _ = address_to_pointer %a
145
148
/// end_borrow %b // the only interior use of %f
@@ -631,17 +634,46 @@ extension AddressUseVisitor {
631
634
}
632
635
}
633
636
634
- /// Enumerate all special cases of ownership uses in a visitor
635
- /// API. This encourages anyone who needs to analyze ownership uses to
636
- /// think about all of the special cases, many of which result
637
- /// from phis of borrowed values. Relying on linear lifetimes, which
638
- /// results from running "lifetime completion", allows you to ignore
639
- /// those cases.
637
+ /// For OwnershipUseVisitor API entry points that operate on a
638
+ /// use, Isinnerlifetime indicates whether the value being used is
639
+ /// defined by the "outer" OSSA lifetime or an inner borrow scope.
640
+ ///
641
+ /// When the OwnershipUseVisitor is invoked on an outer value
642
+ /// (visitUsesOfOuter(value:)), it visits all the uses of that value
643
+ /// and also visits the lifetime-ending uses of any inner borrow
644
+ /// scopes. This provides a complete set of liveness "use points":
645
+ ///
646
+ /// %0 = begin_borrow %outerValue
647
+ /// %1 = begin_borrow %0
648
+ /// end_borrow %1 // inner "use point" of %0
649
+ /// end_borrow %0 // outer use of %1
650
+ ///
651
+ /// This becomes more complicated with reborrows and closures. The
652
+ /// implementation can simply rely on IsInnerLifetime to know whether
653
+ /// the value being used is part of the outer lifetimes vs. its inner
654
+ /// lifetimes. This is important, for example, if the implementation
655
+ /// wants to know if the use ends the lifetime of the outer value.
656
+ ///
657
+ /// Visitor implementations treat inner and outer uses differently. It
658
+ /// may, for example, assume that inner lifetimes are complete
659
+ /// and therefore only care about the lifetime-ending uses.
660
+ enum IsInnerLifetime {
661
+ case outerLifetime
662
+ case innerLifetime
663
+ }
664
+
665
+ /// Package operand ownership into a visitor API.
666
+ ///
667
+ /// Code that relies on effect of a use on ownership of its value
668
+ /// should conform to this visitor. This ensures correct handling for
669
+ /// special cases involving borrow scopes and interior pointers.
640
670
///
641
- /// To visit a value's uses:
671
+ /// `visitUsesOfOuter(value:)` is the main entry point.
642
672
///
643
- /// - visitUsesOfOuter(value:)
644
- /// - visitUsesOfInner(value:)
673
+ /// `visitUsesOfInner(value:)` is called back for each inner borrow
674
+ /// scope. The implementation may or may not decide to recurse through
675
+ /// reborrows and nested borrow scopes, calling back to
676
+ /// `visitUsesOfInner(value:)` as needed.
645
677
///
646
678
/// Visitors need to implement:
647
679
///
@@ -652,14 +684,14 @@ extension AddressUseVisitor {
652
684
///
653
685
/// This only visits the first level of uses. The implementation may
654
686
/// transitively visit forwarding operations in its implementation of
655
- /// `visitForwarding(operand:_)` and `visitReborrow(operand:_)`,
656
- /// calling back to `visitUsesOfOuter(value:)` or
687
+ /// `visitForwarding(operand:_)` and `visitReborrow(operand:_)`, which
688
+ /// can recursively call back to `visitUsesOfOuter(value:)` or
657
689
/// `visitUsesOfInner(value:)`.
658
690
///
659
- /// For uses that begin a borrow or access scope, this skips ahead to
660
- /// the end of the scope. To record incomplete or dead inner scopes
661
- /// (no scope-ending use on some path), the implementation must
662
- /// override handleInner(borrow:).
691
+ /// For uses that begin a borrow or access scope, this correctly
692
+ /// "skips ahead" to the end of the scope. To record incomplete or
693
+ /// dead inner scopes (no scope-ending use on some path), the
694
+ /// implementation must override ` handleInner(borrow:)` .
663
695
protocol OwnershipUseVisitor {
664
696
var _context : Context { get }
665
697
@@ -698,30 +730,36 @@ protocol OwnershipUseVisitor {
698
730
/// result and implement this as a fatalError.
699
731
mutating func visitPointerEscape( use: Operand ) -> WalkResult
700
732
701
- /// Handle begin_borrow, load_borrow, store_borrow, begin_apply.
733
+ /// Handles any of:
702
734
///
703
- /// Handle an inner adjacent phi where the original OSSA def is a
704
- /// phi in the same block
735
+ /// - begin_borrow, load_borrow, store_borrow, begin_apply.
705
736
///
706
- /// Handle a reborrow of an inner borrow scope or inner adjacent phi
737
+ /// - an inner adjacent phi (where the value currently being visited is an
738
+ /// outer adjacent phi in the same block).
739
+ ///
740
+ /// - a reborrow of an inner borrow scope or a reborrow of an inner
741
+ /// adjacent phi.
707
742
///
708
743
/// If this returns .continueWalk, then visit(use:) will be called
709
- /// on the scope ending operands.
744
+ /// on the scope- ending operands.
710
745
///
711
- /// Allows the implementation to complete inner scopes before considering
712
- /// their scope ending operations as uses of the outer scope.
746
+ /// Allows the implementation to complete an inner scope before
747
+ /// visiting its scope-ending operations as uses of the outer
748
+ /// scope. The implementation may add uses to the inner scope, but
749
+ /// it may not modify the use-list containing \p address or in any
750
+ /// outer scopes.
713
751
mutating func handleInner( borrow: BeginBorrowValue ) -> WalkResult
714
752
715
753
/// Handle begin_access.
716
754
///
717
755
/// If this returns .continueWalk, then visit(use:) will be called
718
756
/// on the scope ending operands.
719
757
///
720
- /// Allows the implementation to complete inner scopes before considering
721
- /// their scope ending operations as uses of the outer scope.
722
- ///
723
- /// This may add uses to the inner scope, but it may not modify the use-list
724
- /// containing \p address or in any outer scopes.
758
+ /// Allows the implementation to complete an inner scope before
759
+ /// visiting its scope- ending operations as uses of the outer
760
+ /// scope. The implementation may add uses to the inner scope, but
761
+ /// it may not modify the use-list containing \p address or in any
762
+ /// outer scopes.
725
763
mutating func handleAccess( address: BeginAccessInst ) -> WalkResult
726
764
}
727
765
0 commit comments