Skip to content

Commit eab76a4

Browse files
authored
Merge pull request #82049 from atrick/lifedep-fix-yield
LifetimeDependenceScopeFixup: handle yielded value copies.
2 parents f387e35 + 227f802 commit eab76a4

File tree

5 files changed

+74
-5
lines changed

5 files changed

+74
-5
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ private struct ScopeExtension {
329329
// violation, and that subsequent optimizations do not shrink the inner access `%a1`.
330330
extension ScopeExtension {
331331
mutating func extendScopes(dependence: LifetimeDependence) -> Bool {
332-
log("Scope fixup for lifetime dependent instructions: \(dependence)")
332+
log("Scope fixup for lifetime dependent instructions:\n\(dependence)")
333333

334334
gatherExtensions(dependence: dependence)
335335

@@ -1076,7 +1076,7 @@ private struct LifetimeDependentUseWalker : LifetimeDependenceDefUseWalker {
10761076
}
10771077

10781078
mutating func yieldedDependence(result: Operand) -> WalkResult {
1079-
return .continueWalk
1079+
return visitor(result)
10801080
}
10811081

10821082
mutating func storeToYieldDependence(address: Value, of operand: Operand) -> WalkResult {

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
211211
return scopedValue
212212
}
213213

214+
/// Returns non-nil if this borrowing instruction produces an guaranteed dependent value and does not have immediate
215+
/// scope-ending uses. Finding the borrow scope in such cases requires recursively following uses of the guaranteed
216+
/// value.
214217
var dependentValue: Value? {
215218
switch self {
216219
case .borrowedFrom(let bfi):

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -834,9 +834,15 @@ extension LifetimeDependenceDefUseWalker {
834834
return walkDownUses(of: bfi, using: operand)
835835
case let .storeBorrow(sbi):
836836
return walkDownAddressUses(of: sbi)
837-
case .beginApply:
838-
// Skip the borrow scope; the type system enforces non-escapable
839-
// arguments.
837+
case let .beginApply(bai):
838+
// First, visit the uses of any non-Escapable yields. The yielded value may be copied and used outside the
839+
// coroutine scope. Now, visit the uses of the begin_apply token. This adds the coroutine scope itself to the
840+
for yield in bai.yieldedValues {
841+
if walkDownUses(of: yield, using: operand) == .abortWalk {
842+
return .abortWalk
843+
}
844+
}
845+
// lifetime to account for the scope of any arguments.
840846
return visitInnerBorrowUses(of: borrowInst, operand: operand)
841847
case .partialApply, .markDependence:
842848
fatalError("OwnershipUseVisitor should bypass partial_apply [on_stack] "

test/SILOptimizer/lifetime_dependence/lifetime_dependence_util.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ bb0(%0 : @guaranteed $NE):
252252

253253
// CHECK-LABEL: dependence_coroutine: lifetime_dependence_use with: %0
254254
// CHECK: LifetimeDependence uses of: %0 = argument of bb0 : $NE
255+
// CHECK-NEXT: Leaf use: operand #0 of destroy_value %{{.*}} : $NE
255256
// CHECK-NEXT: Leaf use: operand #0 of %{{.*}} = end_apply %{{.*}} as $()
256257
// CHECK-NEXT: dependence_coroutine: lifetime_dependence_use with: %0
257258
sil [ossa] @dependence_coroutine : $@convention(thin) (@guaranteed NE) -> () {

test/SILOptimizer/lifetime_dependence/scope_fixup.sil

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ sil @useNE : $@convention(thin) (@guaranteed NE) -> ()
7272

7373
sil [ossa] @Wrapper_init : $@convention(method) (@owned NE, @thin Wrapper.Type) -> @lifetime(copy 0) @owned Wrapper
7474

75+
sil @yieldWrapper : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
76+
sil @yieldNE : $@yield_once @convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) @yields @guaranteed NE
77+
7578
sil [ossa] @NCContainer_ne_read : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed NE
7679

7780
sil @yieldRawSpan : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan
@@ -554,3 +557,59 @@ bb0(%0 : $*Holder):
554557
destroy_value %holder
555558
return %md
556559
}
560+
561+
// Test a nested dependent coroutine. The inner coroutine yields a
562+
// value (NE) that inherits its dependency from the outer yield
563+
// (Wrapper). All copies of the inner yield must be used within the
564+
// scope of the outer coroutine. The outer end_apply must be extended
565+
// below the call to 'useNE'.
566+
//
567+
// CHECK-LABEL: sil hidden [ossa] @testCopiedYield : $@convention(thin) (@owned NCContainer) -> () {
568+
// CHECK: bb0(%0 : @owned $NCContainer):
569+
// CHECK: [[BOX:%[0-9]+]] = alloc_box ${ var NCContainer }, var, name "nc"
570+
// CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [unknown]
571+
// CHECK: [[LB:%[0-9]+]] = load_borrow [unchecked]
572+
// CHECK: ([[YIELD:%[0-9]+]], [[TOKEN:%[0-9]+]]) = begin_apply %7(%6) : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
573+
// CHECK: mark_dependence [unresolved] [[YIELD]] on [[TOKEN]]
574+
// CHECK: ([[NE:%[0-9]+]], %{{.*}}) = begin_apply %13(%12) : $@yield_once @convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) @yields @guaranteed NE
575+
// CHECK: destroy_value
576+
// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(thin) (@guaranteed NE) -> ()
577+
// CHECK: destroy_value %{{.*}}
578+
// CHECK: end_apply [[TOKEN]] as $()
579+
// CHECK: end_borrow [[LB]]
580+
// CHECK: end_access [[ACCESS]]
581+
// CHECK: destroy_value [[BOX]]
582+
// CHECK-LABEL: } // end sil function 'testCopiedYield'
583+
sil hidden [ossa] @testCopiedYield : $@convention(thin) (@owned NCContainer) -> () {
584+
bb0(%0 : @owned $NCContainer):
585+
%1 = alloc_box ${ var NCContainer }, var, name "nc"
586+
%2 = begin_borrow [lexical] [var_decl] %1
587+
%3 = project_box %2, 0
588+
store %0 to [init] %3
589+
%5 = begin_access [read] [unknown] %3
590+
%7 = load_borrow [unchecked] %5
591+
592+
%8 = function_ref @yieldWrapper : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
593+
(%9, %10) = begin_apply %8(%7) : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
594+
%11 = mark_dependence [unresolved] %9 on %10
595+
%12 = copy_value %11
596+
%13 = end_apply %10 as $()
597+
end_borrow %7
598+
end_access %5
599+
%16 = move_value [var_decl] %12
600+
601+
%18 = function_ref @yieldNE : $@yield_once @convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) @yields @guaranteed NE
602+
(%19, %20) = begin_apply %18(%16) : $@yield_once @convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) @yields @guaranteed NE
603+
%21 = copy_value %19
604+
%22 = end_apply %20 as $()
605+
destroy_value %16
606+
%23 = move_value [var_decl] %21
607+
608+
%25 = function_ref @useNE : $@convention(thin) (@guaranteed NE) -> ()
609+
%26 = apply %25(%23) : $@convention(thin) (@guaranteed NE) -> ()
610+
destroy_value %23
611+
end_borrow %2
612+
destroy_value %1
613+
%31 = tuple ()
614+
return %31
615+
}

0 commit comments

Comments
 (0)