LifetimeDependenceScopeFixup: extend store_borrow scopes

This is require to handle '@'_addressable arguments on the caller side.
This commit is contained in:
Andrew Trick
2025-03-03 15:58:42 -08:00
parent 7b83c09fe9
commit 1b51aa8829
2 changed files with 117 additions and 15 deletions

View File

@@ -249,7 +249,7 @@ private struct ScopeExtension {
private extension LifetimeDependence.Scope { private extension LifetimeDependence.Scope {
/// The instruction that introduces an extendable scope. This returns a non-nil scope introducer for /// The instruction that introduces an extendable scope. This returns a non-nil scope introducer for
/// Extendable.nestedScopes. /// ScopeExtension.nestedScopes.
var extendableBegin: Instruction? { var extendableBegin: Instruction? {
switch self { switch self {
case let .access(beginAccess): case let .access(beginAccess):
@@ -258,6 +258,17 @@ private extension LifetimeDependence.Scope {
return beginBorrow.value.definingInstruction! return beginBorrow.value.definingInstruction!
case let .yield(yieldedValue): case let .yield(yieldedValue):
return yieldedValue.definingInstruction! return yieldedValue.definingInstruction!
case let .initialized(initializer):
switch initializer {
case let .store(initializingStore: store, initialAddress: _):
if let sb = store as? StoreBorrowInst {
return sb
}
return nil
case .argument, .yield:
// TODO: extend indirectly yielded scopes.
return nil
}
default: default:
return nil return nil
} }
@@ -277,17 +288,8 @@ private extension LifetimeDependence.Scope {
let accessExtension = gatherAccessExtension(beginAccess: beginAccess, innerScopes: &innerScopes) let accessExtension = gatherAccessExtension(beginAccess: beginAccess, innerScopes: &innerScopes)
return SingleInlineArray(element: accessExtension) return SingleInlineArray(element: accessExtension)
case let .borrowed(beginBorrow): case let .borrowed(beginBorrow):
let borrowedValue = beginBorrow.baseOperand!.value return gatherBorrowExtension(borrowedValue: beginBorrow.baseOperand!.value, innerScopes: &innerScopes, context)
let enclosingScope = LifetimeDependence.Scope(base: borrowedValue, context)
innerScopes.push(self)
var innerBorrowScopes = innerScopes
innerBorrowScopes.push(enclosingScope)
if let extensions = enclosingScope.gatherExtensions(innerScopes: innerBorrowScopes, context) {
return extensions
}
// This is the outermost scope to be extended because gatherExtensions did not find an enclosing scope.
return SingleInlineArray(element: getOuterExtension(owner: enclosingScope.parentValue, nestedScopes: innerScopes,
context))
case let .yield(yieldedValue): case let .yield(yieldedValue):
innerScopes.push(self) innerScopes.push(self)
var extensions = SingleInlineArray<ScopeExtension>() var extensions = SingleInlineArray<ScopeExtension>()
@@ -300,6 +302,17 @@ private extension LifetimeDependence.Scope {
extensions.append(contentsOf: gatherOperandExtension(on: operand, innerScopes: innerScopes, context)) extensions.append(contentsOf: gatherOperandExtension(on: operand, innerScopes: innerScopes, context))
} }
return extensions return extensions
case let .initialized(initializer):
switch initializer {
case let .store(initializingStore: store, initialAddress: _):
if let sb = store as? StoreBorrowInst {
return gatherBorrowExtension(borrowedValue: sb.source, innerScopes: &innerScopes, context)
}
return nil
case .argument, .yield:
// TODO: extend indirectly yielded scopes.
return nil
}
default: default:
return nil return nil
} }
@@ -360,6 +373,23 @@ private extension LifetimeDependence.Scope {
} }
return ScopeExtension(owner: outerBeginAccess, nestedScopes: innerScopes, dependsOnArg: nil) return ScopeExtension(owner: outerBeginAccess, nestedScopes: innerScopes, dependsOnArg: nil)
} }
func gatherBorrowExtension(borrowedValue: Value,
innerScopes: inout SingleInlineArray<LifetimeDependence.Scope>,
_ context: FunctionPassContext)
-> SingleInlineArray<ScopeExtension> {
let enclosingScope = LifetimeDependence.Scope(base: borrowedValue, context)
innerScopes.push(self)
var innerBorrowScopes = innerScopes
innerBorrowScopes.push(enclosingScope)
if let extensions = enclosingScope.gatherExtensions(innerScopes: innerBorrowScopes, context) {
return extensions
}
// This is the outermost scope to be extended because gatherExtensions did not find an enclosing scope.
return SingleInlineArray(element: getOuterExtension(owner: enclosingScope.parentValue, nestedScopes: innerScopes,
context))
}
} }
/// Compute the range of the a scope owner. Nested scopes must stay within this range. /// Compute the range of the a scope owner. Nested scopes must stay within this range.
@@ -584,6 +614,17 @@ private extension LifetimeDependence.Scope {
case let .yield(yieldedValue): case let .yield(yieldedValue):
let beginApply = yieldedValue.definingInstruction as! BeginApplyInst let beginApply = yieldedValue.definingInstruction as! BeginApplyInst
return beginApply.createEnd(builder, context) return beginApply.createEnd(builder, context)
case let .initialized(initializer):
switch initializer {
case let .store(initializingStore: store, initialAddress: _):
if let sb = store as? StoreBorrowInst {
return builder.createEndBorrow(of: sb)
}
return nil
case .argument, .yield:
// TODO: extend indirectly yielded scopes.
return nil
}
default: default:
return nil return nil
} }

View File

@@ -1,11 +1,15 @@
// RUN: %target-sil-opt %s \ // RUN: %target-sil-opt \
// RUN: --lifetime-dependence-scope-fixup \ // RUN: -lifetime-dependence-scope-fixup \
// RUN: -sil-verify-all \ // RUN: -sil-verify-all \
// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-experimental-feature LifetimeDependence \
// RUN: 2>&1 | %FileCheck %s // RUN: -enable-experimental-feature AddressableParameters \
// RUN: -enable-experimental-feature AddressableTypes \
// RUN: %s | %FileCheck %s
// REQUIRES: swift_in_compiler // REQUIRES: swift_in_compiler
// REQUIRES: swift_feature_LifetimeDependence // REQUIRES: swift_feature_LifetimeDependence
// REQUIRES: swift_feature_AddressableParameters
// REQUIRES: swift_feature_AddressableTypes
// Test the SIL representation for lifetime dependence scope fixup. // Test the SIL representation for lifetime dependence scope fixup.
@@ -15,6 +19,10 @@ import Builtin
import Swift import Swift
struct NE : ~Escapable { struct NE : ~Escapable {
var p: UnsafeRawPointer
@lifetime(immortal)
init()
} }
struct Wrapper : ~Escapable { struct Wrapper : ~Escapable {
@@ -31,6 +39,22 @@ struct NCContainer : ~Copyable {
var wrapper: Wrapper { get } // _read var wrapper: Wrapper { get } // _read
} }
struct TrivialHolder {
var pointer: UnsafeRawPointer
}
struct Holder {
var object: AnyObject
}
@_addressableForDependencies
struct AddressableForDeps {}
sil @getPtr : $@convention(thin) () -> @out UnsafeRawPointer
sil @getSpan : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
sil @useNE : $@convention(thin) (@guaranteed NE) -> ()
sil [ossa] @Wrapper_init : $@convention(method) (@owned NE, @thin Wrapper.Type) -> @lifetime(copy 0) @owned Wrapper sil [ossa] @Wrapper_init : $@convention(method) (@owned NE, @thin Wrapper.Type) -> @lifetime(copy 0) @owned Wrapper
sil [ossa] @NCContainer_ne_read : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed NE sil [ossa] @NCContainer_ne_read : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed NE
@@ -120,3 +144,40 @@ bb0(%0 : $UnsafePointer<Int64>):
%19 = tuple () %19 = tuple ()
return %19 return %19
} }
// =============================================================================
// @_addressable
// =============================================================================
sil [ossa] @addressableArg : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
// CHECK-LABEL: sil hidden [ossa] @testAddressableArg : $@convention(thin) (@guaranteed Holder) -> () {
// CHECK: bb0(%0 : @guaranteed $Holder):
// CHECK: [[ALLOC:%.*]] = alloc_stack $Holder
// CHECK: [[SB:%.*]] = store_borrow %0 to [[ALLOC]]
// CHECK: [[APPLY:%.*]] = apply %{{.*}}([[SB]]) : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
// CHECK: [[MD:%.*]] = mark_dependence [unresolved] [[APPLY]] on [[SB]]
// CHECK: [[MV:%.*]] = move_value [var_decl] [[MD]]
// CHECK: apply %{{.*}}([[MV]]) : $@convention(thin) (@guaranteed NE) -> ()
// CHECK: destroy_value [[MV]]
// CHECK: end_borrow [[SB]]
// CHECK: dealloc_stack [[ALLOC]]
// CHECK-LABEL: } // end sil function 'testAddressableArg'
sil hidden [ossa] @testAddressableArg : $@convention(thin) (@guaranteed Holder) -> () {
bb0(%0 : @guaranteed $Holder):
debug_value %0, let, name "arg", argno 1
%2 = alloc_stack $Holder
%3 = store_borrow %0 to %2
%4 = function_ref @addressableArg : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
%5 = apply %4(%3) : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
%6 = mark_dependence [unresolved] %5 on %3
end_borrow %3
%8 = move_value [var_decl] %6
debug_value %8, let, name "ne"
%useNE = function_ref @useNE : $@convention(thin) (@guaranteed NE) -> ()
%18 = apply %useNE(%8) : $@convention(thin) (@guaranteed NE) -> ()
destroy_value %8
dealloc_stack %2
%99 = tuple ()
return %99 : $()
}