@@ -630,13 +630,17 @@ struct LocalVariableReachableAccess {
630630
631631// Find reaching assignments...
632632extension 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.
634635 func gatherReachingAssignments( for instruction: Instruction , in accessStack: inout Stack < LocalVariableAccess > )
635636 -> Bool {
636637 var blockList = BasicBlockWorklist ( context)
637638 defer { blockList. deinitialize ( ) }
638639
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+ }
640644 if !backwardPropagateEffect( in: instruction. parentBlock, effect: initialEffect, blockList: & blockList,
641645 accessStack: & accessStack) {
642646 return false
@@ -647,7 +651,7 @@ extension LocalVariableReachableAccess {
647651 // lattice: none -> read -> modify -> escape -> assign
648652 //
649653 // `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 .
651655 switch currentEffect {
652656 case . none, . read, . modify, . escape:
653657 break
@@ -695,7 +699,6 @@ extension LocalVariableReachableAccess {
695699 continue
696700 case . assign:
697701 accessStack. push ( accessInfo. access)
698- break
699702 case . escape:
700703 break
701704 }
@@ -709,25 +712,31 @@ extension LocalVariableReachableAccess {
709712extension LocalVariableReachableAccess {
710713 /// This performs a forward CFG walk to find known reachable uses from `assignment`. This ignores aliasing and
711714 /// 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 ,
713719 in accessStack: inout Stack < LocalVariableAccess > ) {
714720 if let modifyInst = assignment. instruction {
715- _ = gatherReachableUses ( after: modifyInst, in: & accessStack, allowEscape: true )
721+ _ = gatherReachableUses ( after: modifyInst, in: & accessStack, lifetime: true )
722+ return
716723 }
717- gatherKnownReachableUsesFromEntry ( in: & accessStack)
724+ gatherKnownLifetimeUsesFromEntry ( in: & accessStack)
718725 }
719726
720727 /// This performs a forward CFG walk to find known reachable uses from the function entry. This ignores aliasing and
721728 /// escapes.
722- private func gatherKnownReachableUsesFromEntry ( in accessStack: inout Stack < LocalVariableAccess > ) {
729+ private func gatherKnownLifetimeUsesFromEntry ( in accessStack: inout Stack < LocalVariableAccess > ) {
723730 assert ( accessMap. liveInAccess!. kind == . incomingArgument, " only an argument access is live in to the function " )
724731 let firstInst = accessMap. function. entryBlock. instructions. first!
725- _ = gatherReachableUses ( onOrAfter: firstInst, in: & accessStack, allowEscape : true )
732+ _ = gatherReachableUses ( onOrAfter: firstInst, in: & accessStack, lifetime : true )
726733 }
727734
728735 /// This performs a forward CFG walk to find all reachable uses of `modifyInst`. `modifyInst` may be a `begin_access
729736 /// [modify]` or instruction that initializes the local variable.
730737 ///
738+ /// This does not include the destroy or reassignment of the value set by `modifyInst`.
739+ ///
731740 /// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` are
732741 /// reachable from `modifyInst`.
733742 ///
@@ -749,37 +758,40 @@ extension LocalVariableReachableAccess {
749758 if accessInfo. hasEscaped! {
750759 return false
751760 }
752- return gatherReachableUses ( after: modifyInst, in: & accessStack, allowEscape : false )
761+ return gatherReachableUses ( after: modifyInst, in: & accessStack, lifetime : false )
753762 }
754763
755764 /// This performs a forward CFG walk to find all uses of this local variable reachable after `begin`.
756765 ///
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.
758770 private func gatherReachableUses( after begin: Instruction , in accessStack: inout Stack < LocalVariableAccess > ,
759- allowEscape : Bool ) -> Bool {
771+ lifetime : Bool ) -> Bool {
760772 if let term = begin as? TermInst {
761773 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 ) {
763775 return false
764776 }
765777 }
766778 return true
767779 } else {
768- return gatherReachableUses ( onOrAfter: begin. next!, in: & accessStack, allowEscape : allowEscape )
780+ return gatherReachableUses ( onOrAfter: begin. next!, in: & accessStack, lifetime : lifetime )
769781 }
770782 }
771783
772784 /// This performs a forward CFG walk to find all uses of this local variable reachable after and including `begin`.
773785 ///
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.
775787 private func gatherReachableUses( onOrAfter begin: Instruction , in accessStack: inout Stack < LocalVariableAccess > ,
776- allowEscape : Bool ) -> Bool {
788+ lifetime : Bool ) -> Bool {
777789 var blockList = BasicBlockWorklist ( context)
778790 defer { blockList. deinitialize ( ) }
779791
780792 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 {
783795 return false
784796 }
785797 forwardPropagateEffect ( in: initialBlock, blockInfo: blockMap [ initialBlock] , effect: initialEffect,
@@ -795,15 +807,15 @@ extension LocalVariableReachableAccess {
795807 case . none:
796808 break
797809 case . escape:
798- if !allowEscape {
810+ if !lifetime {
799811 break
800812 }
801813 fallthrough
802814 case . read, . modify, . assign:
803815 let firstInst = block. instructions. first!
804- currentEffect = forwardScanAccesses ( after: firstInst, accessStack: & accessStack, allowEscape : allowEscape )
816+ currentEffect = forwardScanAccesses ( after: firstInst, accessStack: & accessStack, lifetime : lifetime )
805817 }
806- if !allowEscape , currentEffect == . escape {
818+ if !lifetime , currentEffect == . escape {
807819 return false
808820 }
809821 forwardPropagateEffect ( in: block, blockInfo: blockInfo, effect: currentEffect, blockList: & blockList,
@@ -836,9 +848,9 @@ extension LocalVariableReachableAccess {
836848 }
837849
838850 // 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.
840852 private func forwardScanAccesses( after first: Instruction , accessStack: inout Stack < LocalVariableAccess > ,
841- allowEscape : Bool )
853+ lifetime : Bool )
842854 -> BlockEffect ? {
843855 var currentEffect : BlockEffect ?
844856 for inst in InstructionList ( first: first) {
@@ -848,9 +860,12 @@ extension LocalVariableReachableAccess {
848860 currentEffect = BlockEffect ( for: accessInfo, accessMap. context) . meet ( currentEffect)
849861 switch currentEffect! {
850862 case . assign:
863+ if lifetime {
864+ accessStack. push ( accessInfo. access)
865+ }
851866 return currentEffect
852867 case . escape:
853- if !allowEscape {
868+ if !lifetime {
854869 log ( " Local variable: \( accessMap. allocation) \n escapes at \( inst) " )
855870 return currentEffect
856871 }
0 commit comments