@@ -630,13 +630,17 @@ struct LocalVariableReachableAccess {
630
630
631
631
// Find reaching assignments...
632
632
extension LocalVariableReachableAccess {
633
- // Gather all fully assigned accesses that reach `instruction`.
633
+ // Gather all fully assigned accesses that reach 'instruction'. If 'instruction' is itself a modify access, it is
634
+ // ignored and the nearest assignments above 'instruction' are still gathered.
634
635
func gatherReachingAssignments( for instruction: Instruction , in accessStack: inout Stack < LocalVariableAccess > )
635
636
-> Bool {
636
637
var blockList = BasicBlockWorklist ( context)
637
638
defer { blockList. deinitialize ( ) }
638
639
639
- let initialEffect = backwardScanAccesses ( before: instruction, accessStack: & accessStack)
640
+ var initialEffect : BlockEffect ? = nil
641
+ if let prev = instruction. previous {
642
+ initialEffect = backwardScanAccesses ( before: prev, accessStack: & accessStack)
643
+ }
640
644
if !backwardPropagateEffect( in: instruction. parentBlock, effect: initialEffect, blockList: & blockList,
641
645
accessStack: & accessStack) {
642
646
return false
@@ -647,7 +651,7 @@ extension LocalVariableReachableAccess {
647
651
// lattice: none -> read -> modify -> escape -> assign
648
652
//
649
653
// `blockInfo.effect` is the same as `currentEffect` returned by backwardScanAccesses, except when an early escape
650
- // happens after an assign.
654
+ // happens below an assign, in which case we report the escape here .
651
655
switch currentEffect {
652
656
case . none, . read, . modify, . escape:
653
657
break
@@ -695,7 +699,6 @@ extension LocalVariableReachableAccess {
695
699
continue
696
700
case . assign:
697
701
accessStack. push ( accessInfo. access)
698
- break
699
702
case . escape:
700
703
break
701
704
}
@@ -709,25 +712,31 @@ extension LocalVariableReachableAccess {
709
712
extension LocalVariableReachableAccess {
710
713
/// This performs a forward CFG walk to find known reachable uses from `assignment`. This ignores aliasing and
711
714
/// escapes.
712
- func gatherKnownReachableUses( from assignment: LocalVariableAccess ,
715
+ ///
716
+ /// The known live range is the range in which the assigned value is valid and may be used by dependent values. It
717
+ /// includes the destroy or reassignment of the local.
718
+ func gatherKnownLifetimeUses( from assignment: LocalVariableAccess ,
713
719
in accessStack: inout Stack < LocalVariableAccess > ) {
714
720
if let modifyInst = assignment. instruction {
715
- _ = gatherReachableUses ( after: modifyInst, in: & accessStack, allowEscape: true )
721
+ _ = gatherReachableUses ( after: modifyInst, in: & accessStack, lifetime: true )
722
+ return
716
723
}
717
- gatherKnownReachableUsesFromEntry ( in: & accessStack)
724
+ gatherKnownLifetimeUsesFromEntry ( in: & accessStack)
718
725
}
719
726
720
727
/// This performs a forward CFG walk to find known reachable uses from the function entry. This ignores aliasing and
721
728
/// escapes.
722
- private func gatherKnownReachableUsesFromEntry ( in accessStack: inout Stack < LocalVariableAccess > ) {
729
+ private func gatherKnownLifetimeUsesFromEntry ( in accessStack: inout Stack < LocalVariableAccess > ) {
723
730
assert ( accessMap. liveInAccess!. kind == . incomingArgument, " only an argument access is live in to the function " )
724
731
let firstInst = accessMap. function. entryBlock. instructions. first!
725
- _ = gatherReachableUses ( onOrAfter: firstInst, in: & accessStack, allowEscape : true )
732
+ _ = gatherReachableUses ( onOrAfter: firstInst, in: & accessStack, lifetime : true )
726
733
}
727
734
728
735
/// This performs a forward CFG walk to find all reachable uses of `modifyInst`. `modifyInst` may be a `begin_access
729
736
/// [modify]` or instruction that initializes the local variable.
730
737
///
738
+ /// This does not include the destroy or reassignment of the value set by `modifyInst`.
739
+ ///
731
740
/// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` are
732
741
/// reachable from `modifyInst`.
733
742
///
@@ -749,37 +758,40 @@ extension LocalVariableReachableAccess {
749
758
if accessInfo. hasEscaped! {
750
759
return false
751
760
}
752
- return gatherReachableUses ( after: modifyInst, in: & accessStack, allowEscape : false )
761
+ return gatherReachableUses ( after: modifyInst, in: & accessStack, lifetime : false )
753
762
}
754
763
755
764
/// This performs a forward CFG walk to find all uses of this local variable reachable after `begin`.
756
765
///
757
- /// If `allowEscape` is true, then this returns false if the walk ended early because of a reachable escape.
766
+ /// If `lifetime` is true, then this gathers the full known lifetime, includeing destroys and reassignments ignoring
767
+ /// escapes.
768
+ ///
769
+ /// If `lifetime` is false, then this returns `false` if the walk ended early because of a reachable escape.
758
770
private func gatherReachableUses( after begin: Instruction , in accessStack: inout Stack < LocalVariableAccess > ,
759
- allowEscape : Bool ) -> Bool {
771
+ lifetime : Bool ) -> Bool {
760
772
if let term = begin as? TermInst {
761
773
for succ in term. successors {
762
- if !gatherReachableUses( onOrAfter: succ. instructions. first!, in: & accessStack, allowEscape : allowEscape ) {
774
+ if !gatherReachableUses( onOrAfter: succ. instructions. first!, in: & accessStack, lifetime : lifetime ) {
763
775
return false
764
776
}
765
777
}
766
778
return true
767
779
} else {
768
- return gatherReachableUses ( onOrAfter: begin. next!, in: & accessStack, allowEscape : allowEscape )
780
+ return gatherReachableUses ( onOrAfter: begin. next!, in: & accessStack, lifetime : lifetime )
769
781
}
770
782
}
771
783
772
784
/// This performs a forward CFG walk to find all uses of this local variable reachable after and including `begin`.
773
785
///
774
- /// If `allowEscape ` is true, then this returns false if the walk ended early because of a reachable escape.
786
+ /// If `lifetime ` is true, then this returns false if the walk ended early because of a reachable escape.
775
787
private func gatherReachableUses( onOrAfter begin: Instruction , in accessStack: inout Stack < LocalVariableAccess > ,
776
- allowEscape : Bool ) -> Bool {
788
+ lifetime : Bool ) -> Bool {
777
789
var blockList = BasicBlockWorklist ( context)
778
790
defer { blockList. deinitialize ( ) }
779
791
780
792
let initialBlock = begin. parentBlock
781
- let initialEffect = forwardScanAccesses ( after: begin, accessStack: & accessStack, allowEscape : allowEscape )
782
- if !allowEscape , initialEffect == . escape {
793
+ let initialEffect = forwardScanAccesses ( after: begin, accessStack: & accessStack, lifetime : lifetime )
794
+ if !lifetime , initialEffect == . escape {
783
795
return false
784
796
}
785
797
forwardPropagateEffect ( in: initialBlock, blockInfo: blockMap [ initialBlock] , effect: initialEffect,
@@ -795,15 +807,15 @@ extension LocalVariableReachableAccess {
795
807
case . none:
796
808
break
797
809
case . escape:
798
- if !allowEscape {
810
+ if !lifetime {
799
811
break
800
812
}
801
813
fallthrough
802
814
case . read, . modify, . assign:
803
815
let firstInst = block. instructions. first!
804
- currentEffect = forwardScanAccesses ( after: firstInst, accessStack: & accessStack, allowEscape : allowEscape )
816
+ currentEffect = forwardScanAccesses ( after: firstInst, accessStack: & accessStack, lifetime : lifetime )
805
817
}
806
- if !allowEscape , currentEffect == . escape {
818
+ if !lifetime , currentEffect == . escape {
807
819
return false
808
820
}
809
821
forwardPropagateEffect ( in: block, blockInfo: blockInfo, effect: currentEffect, blockList: & blockList,
@@ -836,9 +848,9 @@ extension LocalVariableReachableAccess {
836
848
}
837
849
838
850
// Check all instructions in this block after and including `begin`. Return a BlockEffect indicating the combined
839
- // effects seen before stopping the scan. An .assign stops the scan. A .escape stops the scan if allowEscape is false.
851
+ // effects seen before stopping the scan. An .assign stops the scan. A .escape stops the scan if lifetime is false.
840
852
private func forwardScanAccesses( after first: Instruction , accessStack: inout Stack < LocalVariableAccess > ,
841
- allowEscape : Bool )
853
+ lifetime : Bool )
842
854
-> BlockEffect ? {
843
855
var currentEffect : BlockEffect ?
844
856
for inst in InstructionList ( first: first) {
@@ -848,9 +860,12 @@ extension LocalVariableReachableAccess {
848
860
currentEffect = BlockEffect ( for: accessInfo, accessMap. context) . meet ( currentEffect)
849
861
switch currentEffect! {
850
862
case . assign:
863
+ if lifetime {
864
+ accessStack. push ( accessInfo. access)
865
+ }
851
866
return currentEffect
852
867
case . escape:
853
- if !allowEscape {
868
+ if !lifetime {
854
869
log ( " Local variable: \( accessMap. allocation) \n escapes at \( inst) " )
855
870
return currentEffect
856
871
}
0 commit comments