@@ -613,11 +613,9 @@ func gatherEnclosingValues(for value: Value,
613
613
in enclosingValues: inout Stack < Value > ,
614
614
_ context: some Context ) {
615
615
616
- var gatherValues = EnclosingValues ( context)
617
- defer { gatherValues. deinitialize ( ) }
618
- var cache = BorrowIntroducers . Cache ( context)
616
+ var cache = EnclosingValues . Cache ( context)
619
617
defer { cache. deinitialize ( ) }
620
- gatherValues . gather ( for: value, in: & enclosingValues, & cache)
618
+ EnclosingValues . gather ( for: value, in: & enclosingValues, & cache, context )
621
619
}
622
620
623
621
/// Find inner adjacent phis in the same block as `enclosingPhi`.
@@ -637,21 +635,61 @@ func gatherInnerAdjacentPhis(for enclosingPhi: Phi,
637
635
638
636
// Find the enclosing values for any value, including reborrows.
639
637
private struct EnclosingValues {
638
+ typealias CachedEnclosingValues = SingleInlineArray < Value >
639
+ struct Cache {
640
+ // Cache the enclosing values already found for each Reborrow.
641
+ var reborrowToEnclosingValues : Dictionary < HashableValue ,
642
+ CachedEnclosingValues >
643
+ // Record recursively followed reborrows to avoid infinite cycles.
644
+ // Reborrows are removed from this set when they are cached.
645
+ var pendingReborrows : ValueSet
646
+
647
+ var borrowIntroducerCache : BorrowIntroducers . Cache
648
+
649
+ init ( _ context: Context ) {
650
+ reborrowToEnclosingValues =
651
+ Dictionary < HashableValue , CachedEnclosingValues > ( )
652
+ pendingReborrows = ValueSet ( context)
653
+ borrowIntroducerCache = BorrowIntroducers . Cache ( context)
654
+ }
655
+
656
+ mutating func deinitialize( ) {
657
+ pendingReborrows. deinitialize ( )
658
+ borrowIntroducerCache. deinitialize ( )
659
+ }
660
+ }
661
+
640
662
var context : Context
641
- var visitedReborrows : ValueSet
663
+ // EnclosingValues instances are recursively nested in order to
664
+ // find outer adjacent phis. Each instance populates a separate
665
+ // 'enclosingValeus' set. The same value may occur in 'enclosingValues' at
666
+ // multiple levels. Each instance, therefore, needs a separate
667
+ // visited set to avoid adding duplicates.
668
+ var visitedEnclosingValues : Set < HashableValue > = Set ( )
669
+
670
+ static func gather( for value: Value , in enclosingValues: inout Stack < Value > ,
671
+ _ cache: inout Cache , _ context: Context ) {
672
+ var gatherValues = EnclosingValues ( context: context)
673
+ gatherValues. gather ( for: value, in: & enclosingValues, & cache)
674
+ }
642
675
643
- init ( _ context: Context ) {
644
- self . context = context
645
- self . visitedReborrows = ValueSet ( context)
676
+ private mutating func push( _ enclosingValue: Value ,
677
+ in enclosingValues: inout Stack < Value > ) {
678
+ if visitedEnclosingValues. insert ( enclosingValue. hashable) . inserted {
679
+ enclosingValues. push ( enclosingValue)
680
+ }
646
681
}
647
682
648
- mutating func deinitialize( ) {
649
- visitedReborrows. deinitialize ( )
683
+ private mutating func push< S: Sequence > ( contentsOf other: S ,
684
+ in enclosingValues: inout Stack < Value > ) where S. Element == Value {
685
+ for elem in other {
686
+ push ( elem, in: & enclosingValues)
687
+ }
650
688
}
651
689
652
690
mutating func gather( for value: Value ,
653
691
in enclosingValues: inout Stack < Value > ,
654
- _ cache: inout BorrowIntroducers . Cache ) {
692
+ _ cache: inout Cache ) {
655
693
if value is Undef || value. ownership != . guaranteed {
656
694
return
657
695
}
@@ -660,7 +698,7 @@ private struct EnclosingValues {
660
698
case let . beginBorrow( bbi) :
661
699
// Gather the outer enclosing borrow scope.
662
700
BorrowIntroducers . gather ( for: bbi. operand. value, in: & enclosingValues,
663
- & cache, context)
701
+ & cache. borrowIntroducerCache , context)
664
702
case . loadBorrow, . beginApply, . functionArgument:
665
703
// There is no enclosing value on this path.
666
704
break
@@ -670,7 +708,7 @@ private struct EnclosingValues {
670
708
} else {
671
709
// Handle forwarded guaranteed values.
672
710
BorrowIntroducers . gather ( for: value, in: & enclosingValues,
673
- & cache, context)
711
+ & cache. borrowIntroducerCache , context)
674
712
}
675
713
}
676
714
@@ -724,16 +762,25 @@ private struct EnclosingValues {
724
762
//
725
763
// gather(forReborrow: %reborrow) finds (%outerReborrow, %outerBorrowB).
726
764
//
765
+ // This implementation mirrors BorrowIntroducers.gather(forPhi:in:).
766
+ // The difference is that this performs use-def recursion over
767
+ // reborrows rather, and at each step, it finds the enclosing values
768
+ // of the reborrow operands rather than the borrow introducers of
769
+ // the guaranteed phi.
727
770
private mutating func gather( forReborrow reborrow: Phi ,
728
771
in enclosingValues: inout Stack < Value > ,
729
- _ cache: inout BorrowIntroducers . Cache ) {
772
+ _ cache: inout Cache ) {
730
773
731
- guard visitedReborrows. insert ( reborrow. value) else {
774
+ // Phi cycles are skipped. They cannot contribute any new introducer.
775
+ if !cache. pendingReborrows. insert ( reborrow. value) {
776
+ return
777
+ }
778
+ if let cachedEnclosingValues =
779
+ cache. reborrowToEnclosingValues [ reborrow. value. hashable] {
780
+ push ( contentsOf: cachedEnclosingValues, in: & enclosingValues)
732
781
return
733
782
}
734
- // avoid duplicates in the enclosingValues set.
735
- var pushedEnclosingValues = ValueSet ( context)
736
- defer { pushedEnclosingValues. deinitialize ( ) }
783
+ assert ( enclosingValues. isEmpty)
737
784
738
785
// Find the enclosing introducer for each reborrow operand, and
739
786
// remap it to the enclosing introducer for the successor block.
@@ -743,14 +790,20 @@ private struct EnclosingValues {
743
790
defer {
744
791
incomingEnclosingValues. deinitialize ( )
745
792
}
746
- gather ( for: incomingValue, in: & incomingEnclosingValues, & cache)
747
- mapToPhi ( predecessor: pred,
748
- incomingValues: incomingEnclosingValues) . forEach {
749
- if pushedEnclosingValues. insert ( $0) {
750
- enclosingValues. append ( $0)
751
- }
752
- }
793
+ EnclosingValues . gather ( for: incomingValue, in: & incomingEnclosingValues,
794
+ & cache, context)
795
+ push ( contentsOf: mapToPhi ( predecessor: pred,
796
+ incomingValues: incomingEnclosingValues) ,
797
+ in: & enclosingValues)
753
798
}
799
+ { cachedIntroducers in
800
+ enclosingValues. forEach { cachedIntroducers. push ( $0) }
801
+ } ( & cache. reborrowToEnclosingValues [ reborrow. value. hashable,
802
+ default: CachedEnclosingValues ( ) ] )
803
+
804
+ // Remove this reborrow from the pending set. It may be visited
805
+ // again at a different level of recursion.
806
+ cache. pendingReborrows. erase ( reborrow. value)
754
807
}
755
808
}
756
809
0 commit comments