Skip to content

Commit 4b5d519

Browse files
authored
Merge pull request #80782 from atrick/rdar143992296-addressliverange
Fix AddressOwnershipLiveRange to include the full range.
2 parents 3546c33 + c9279d9 commit 4b5d519

File tree

5 files changed

+140
-43
lines changed

5 files changed

+140
-43
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,11 @@ enum AddressOwnershipLiveRange : CustomStringConvertible {
501501
}
502502
}
503503

504+
/// Return the live range of the addressable value that reaches 'begin', not including 'begin', which may itself be an
505+
/// access of the address.
506+
///
507+
/// The range ends at the destroy or reassignment of the addressable value.
508+
///
504509
/// Return nil if the live range is unknown.
505510
static func compute(for address: Value, at begin: Instruction,
506511
_ localReachabilityCache: LocalVariableReachabilityCache,
@@ -624,7 +629,7 @@ extension AddressOwnershipLiveRange {
624629
var reachableUses = Stack<LocalVariableAccess>(context)
625630
defer { reachableUses.deinitialize() }
626631

627-
localReachability.gatherKnownReachableUses(from: assignment, in: &reachableUses)
632+
localReachability.gatherKnownLifetimeUses(from: assignment, in: &reachableUses)
628633

629634
let assignmentInst = assignment.instruction ?? allocation.parentFunction.entryBlock.instructions.first!
630635
var range = InstructionRange(begin: assignmentInst, context)

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,8 @@ extension LifetimeDependenceDefUseWalker {
930930
// of its forwarded address has were visited by LocalVariableAccessWalker and recorded as separate local accesses.
931931
return .continueWalk
932932
case .store:
933-
let si = localAccess.operand!.instruction as! StoringInstruction
934-
assert(si.sourceOperand == initialValue, "the only reachable store should be the current assignment")
933+
// A store does not use the previous in-memory value.
934+
return .continueWalk
935935
case .apply:
936936
return visitAppliedUse(of: localAccess.operand!, by: localAccess.instruction as! FullApplySite)
937937
case .escape:
@@ -946,7 +946,6 @@ extension LifetimeDependenceDefUseWalker {
946946
case .incomingArgument:
947947
fatalError("Incoming arguments are never reachable")
948948
}
949-
return .continueWalk
950949
}
951950

952951
private mutating func visitAppliedUse(of operand: Operand, by apply: FullApplySite) -> WalkResult {

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -630,13 +630,17 @@ struct LocalVariableReachableAccess {
630630

631631
// Find reaching assignments...
632632
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.
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 {
709712
extension 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
}

test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,40 @@ struct View : ~Escapable {
4040
}
4141
}
4242

43-
struct MutableView : ~Copyable, ~Escapable {
44-
let ptr: UnsafeRawBufferPointer
43+
struct NCMutableContainer : ~Copyable {
44+
let ptr: UnsafeMutableRawBufferPointer
4545
let c: Int
46-
@lifetime(borrow ptr)
47-
init(_ ptr: UnsafeRawBufferPointer, _ c: Int) {
46+
init(_ ptr: UnsafeMutableRawBufferPointer, _ c: Int) {
4847
self.ptr = ptr
4948
self.c = c
5049
}
50+
}
51+
52+
struct MutableView : ~Copyable, ~Escapable {
53+
let ptr: UnsafeMutableRawBufferPointer
54+
@lifetime(borrow ptr)
55+
init(_ ptr: UnsafeMutableRawBufferPointer) {
56+
self.ptr = ptr
57+
}
5158
@lifetime(copy otherBV)
52-
init(_ otherBV: borrowing View) {
59+
init(_ otherBV: borrowing MutableView) {
5360
self.ptr = otherBV.ptr
54-
self.c = otherBV.c
5561
}
56-
init(_ k: borrowing NCContainer) {
62+
init(_ k: borrowing NCMutableContainer) {
5763
self.ptr = k.ptr
58-
self.c = k.c
64+
}
65+
}
66+
67+
extension MutableView {
68+
@lifetime(&self)
69+
mutating public func update() -> Self {
70+
return unsafe MutableView(ptr)
5971
}
6072
}
6173

6274
func use(_ o : borrowing View) {}
6375
func mutate(_ x: inout NCContainer) { }
76+
func mutate(_ x: inout NCMutableContainer) { }
6477
func mutate(_ x: inout View) { }
6578
func consume(_ o : consuming View) {}
6679
func use(_ o : borrowing MutableView) {}
@@ -125,9 +138,9 @@ func test3(_ a: Array<Int>) {
125138
}
126139
}
127140

128-
func test4(_ a: Array<Int>) {
129-
a.withUnsafeBytes {
130-
var x = NCContainer($0, a.count)
141+
func test4(_ a: inout Array<Int>) {
142+
a.withUnsafeMutableBytes {
143+
var x = NCMutableContainer($0, $0.count)
131144
mutate(&x)
132145
let view = MutableView(x)
133146
use(view)
@@ -180,11 +193,11 @@ func test7(_ a: UnsafeRawBufferPointer) {
180193
mutate(&x)
181194
}
182195

183-
func test8(_ a: Array<Int>) {
184-
a.withUnsafeBytes {
185-
var x = View($0, a.count)
196+
func test8(_ a: inout Array<Int>) {
197+
a.withUnsafeMutableBytes {
198+
var x = View(UnsafeRawBufferPointer(start: $0.baseAddress!, count: $0.count), $0.count)
186199
mutate(&x)
187-
let view = MutableView(x)
200+
let view = MutableView($0)
188201
use(view)
189202
consume(view)
190203
}
@@ -245,3 +258,29 @@ func testPointeeDependenceOnMutablePointer(p: UnsafePointer<Int64>) {
245258
_ = ptr.pointee
246259
_ = ptr
247260
}
261+
262+
// CHECK-LABEL: sil hidden @$s31lifetime_dependence_scope_fixup16testReassignment1bySw_tF : $@convention(thin) (UnsafeMutableRawBufferPointer) -> () {
263+
// CHECK: bb0(%0 : $UnsafeMutableRawBufferPointer):
264+
// CHECK: [[VAR:%.*]] = alloc_stack [lexical] [var_decl] $MutableView, var, name "span", type $MutableView
265+
// CHECK: apply %{{.*}}(%0, %{{.*}}) : $@convention(method) (UnsafeMutableRawBufferPointer, @thin MutableView.Type) -> @lifetime(borrow 0) @owned MutableView
266+
// CHECK: [[ACCESS1:%.*]] = begin_access [modify] [static] [[VAR]] : $*MutableView
267+
// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(method) (@inout MutableView) -> @lifetime(borrow 0) @owned MutableView
268+
// CHECK: [[LD1:%.*]] = load %{{.*}} : $*MutableView
269+
// CHECK: apply %{{.*}}([[LD1]]) : $@convention(thin) (@guaranteed MutableView) -> ()
270+
// CHECK: end_access [[ACCESS1]] : $*MutableView
271+
// CHECK: [[ACCESS2:%.*]] = begin_access [modify] [static] [[VAR]] : $*MutableView
272+
// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(method) (@inout MutableView) -> @lifetime(borrow 0) @owned MutableView
273+
// CHECK: [[LD2:%.*]] = load %{{.*}} : $*MutableView
274+
// CHECK: apply %{{.*}}([[LD2]]) : $@convention(thin) (@guaranteed MutableView) -> ()
275+
// CHECK: end_access [[ACCESS2]] : $*MutableView
276+
// CHECK: destroy_addr [[VAR]] : $*MutableView
277+
// CHECK-LABEL: } // end sil function '$s31lifetime_dependence_scope_fixup16testReassignment1bySw_tF'
278+
func testReassignment(b: UnsafeMutableRawBufferPointer) {
279+
var span = MutableView(b)
280+
281+
var sub = span.update()
282+
use(sub)
283+
284+
sub = span.update()
285+
use(sub)
286+
}

test/SILOptimizer/lifetime_dependence/scope_fixup.sil

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct TrivialHolder {
4343
var pointer: UnsafeRawPointer
4444
}
4545

46+
struct A {
47+
init()
48+
}
49+
4650
struct Holder {
4751
var object: AnyObject
4852
}
@@ -59,6 +63,9 @@ sil [ossa] @Wrapper_init : $@convention(method) (@owned NE, @thin Wrapper.Type)
5963

6064
sil [ossa] @NCContainer_ne_read : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed NE
6165

66+
sil @yieldRawSpan : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan
67+
sil @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> ()
68+
6269
// NCContainer.wrapper._read:
6370
// var wrapper: Wrapper {
6471
// _read {
@@ -237,3 +244,35 @@ bb0(%0 : @guaranteed $Holder):
237244
%99 = tuple ()
238245
return %99 : $()
239246
}
247+
248+
// FIXME: extend temporary lifetimes for coroutine access.
249+
//
250+
// CHECK-LABEL: sil [ossa] @testYieldSpan : $@convention(thin) (@in A) -> () {
251+
// CHECK: alloc_stack [lexical] [var_decl] $A
252+
// CHECK: (%{{.*}}, %{{.*}}) = begin_apply %{{.*}}(%{{.*}}) : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan
253+
// CHECK: [[MD:%.*]] = mark_dependence [unresolved]
254+
// CHECK: [[CP:%.*]] = copy_value [[MD]]
255+
// CHECK: apply %{{.*}}([[CP]]) : $@convention(thin) (@guaranteed RawSpan) -> ()
256+
// CHECK: destroy_value [[CP]]
257+
// CHECK: end_apply
258+
// CHECK: destroy_addr
259+
// CHECK-LABEL: } // end sil function 'testYieldSpan'
260+
sil [ossa] @testYieldSpan : $@convention(thin) (@in A) -> () {
261+
bb0(%0 : $*A):
262+
%1 = alloc_stack [lexical] [var_decl] $A
263+
copy_addr [take] %0 to [init] %1
264+
265+
%3 = function_ref @yieldRawSpan : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan
266+
(%4, %5) = begin_apply %3(%1) : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan
267+
%6 = mark_dependence [unresolved] %4 on %5
268+
%7 = copy_value %6
269+
%8 = end_apply %5 as $()
270+
271+
%9 = function_ref @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> ()
272+
%10 = apply %9(%7) : $@convention(thin) (@guaranteed RawSpan) -> ()
273+
destroy_value %7
274+
destroy_addr %1
275+
dealloc_stack %1
276+
%14 = tuple ()
277+
return %14
278+
}

0 commit comments

Comments
 (0)