Skip to content

Commit c440cca

Browse files
authored
Merge pull request #71570 from atrick/lifetime-inherit
LifetimeDependenceInsertion: do not emit inherited dependencies.
2 parents 77cd7b5 + b952888 commit c440cca

12 files changed

+201
-63
lines changed

SwiftCompilerSources/Sources/Basic/Utils.swift

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -114,37 +114,6 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren {
114114
public static func ~=(pattern: StaticString, value: StringRef) -> Bool { value == pattern }
115115
}
116116

117-
//===----------------------------------------------------------------------===//
118-
// Single-Element Inline Array
119-
//===----------------------------------------------------------------------===//
120-
121-
public struct SingleInlineArray<Element>: RandomAccessCollection {
122-
private var singleElement: Element? = nil
123-
private var multipleElements: [Element] = []
124-
125-
public init() {}
126-
127-
public var startIndex: Int { 0 }
128-
public var endIndex: Int {
129-
singleElement == nil ? 0 : multipleElements.count + 1
130-
}
131-
132-
public subscript(_ index: Int) -> Element {
133-
if index == 0 {
134-
return singleElement!
135-
}
136-
return multipleElements[index - 1]
137-
}
138-
139-
public mutating func push(_ element: Element) {
140-
guard singleElement != nil else {
141-
singleElement = element
142-
return
143-
}
144-
multipleElements.append(element)
145-
}
146-
}
147-
148117
//===----------------------------------------------------------------------===//
149118
// Bridging Utilities
150119
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ private struct DiagnoseDependence {
131131
if function.hasUnsafeNonEscapableResult {
132132
return .continueWalk
133133
}
134+
// FIXME: remove this condition once we have a Builtin.dependence,
135+
// which developers should use to model the unsafe
136+
// dependence. Builtin.lifetime_dependence will be lowered to
137+
// mark_dependence [unresolved], which will be checked
138+
// independently. Instead, of this function result check, allow
139+
// isUnsafeApplyResult to be used be mark_dependence [unresolved]
140+
// without checking its dependents.
141+
//
134142
// Allow returning an apply result (@_unsafeNonescapableResult) if
135143
// the calling function has a dependence. This implicitly makes
136144
// the unsafe nonescapable result dependent on the calling

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,16 @@ extension LifetimeDependentApply {
102102
}
103103
}
104104

105-
/// Replace the each dependent apply result with a chain of
106-
/// mark_dependence [nonescaping] instructions; one for each base.
105+
/// If the result of this apply depends on the scope of one or more
106+
/// arguments, then insert a mark_dependence [unresolved] from the
107+
/// result on each argument so that the result is recognized as a
108+
/// dependent value within each scope.
107109
private func insertDependencies(for apply: LifetimeDependentApply,
108110
_ context: FunctionPassContext ) {
109111
precondition(apply.applySite.results.count > 0,
110112
"a lifetime-dependent instruction must have at least one result")
111113

112-
let bases = recursivelyFindDependenceBases(of: apply, context)
114+
let bases = findDependenceBases(of: apply, context)
113115
let builder = Builder(after: apply.applySite, context)
114116
for dependentValue in apply.applySite.resultOrYields {
115117
insertMarkDependencies(value: dependentValue, initializer: nil,
@@ -138,6 +140,31 @@ private func insertDependencies(for apply: LifetimeDependentApply,
138140
}
139141
}
140142

143+
private func findDependenceBases(of apply: LifetimeDependentApply,
144+
_ context: FunctionPassContext)
145+
-> [Value] {
146+
log("Creating dependencies for \(apply.applySite)")
147+
var bases: [Value] = []
148+
for lifetimeArg in apply.getLifetimeArguments() {
149+
switch lifetimeArg.convention {
150+
case .inherit:
151+
continue
152+
case .scope:
153+
// Create a new dependence on the apply's access to the argument.
154+
for varIntoducer in gatherVariableIntroducers(for: lifetimeArg.value,
155+
context) {
156+
if let scope =
157+
LifetimeDependence.Scope(base: varIntoducer, context) {
158+
log("Scoped lifetime from \(lifetimeArg.value)")
159+
log(" scope: \(scope)")
160+
bases.append(scope.parentValue)
161+
}
162+
}
163+
}
164+
}
165+
return bases
166+
}
167+
141168
private func insertMarkDependencies(value: Value, initializer: Instruction?,
142169
bases: [Value], builder: Builder,
143170
_ context: FunctionPassContext) {
@@ -155,6 +182,7 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
155182
}
156183
}
157184

185+
/*
158186
/// Return base values that this return value depends on.
159187
///
160188
/// For lifetime copies, walk up the dependence chain to find the
@@ -212,3 +240,4 @@ private func recursivelyUpdate(scope: LifetimeDependence.Scope,
212240
}
213241
return scope
214242
}
243+
*/

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ extension AccessBase {
199199
case let .stack(allocStack):
200200
baseAddr = allocStack
201201
case let .argument(arg):
202+
guard arg.convention.isIndirectOut else {
203+
return nil
204+
}
202205
baseAddr = arg
203206
default:
204207
return nil
@@ -208,7 +211,10 @@ extension AccessBase {
208211
== .abortWalk {
209212
return nil
210213
}
211-
return (initialAddress: baseAddr, initializingStore: walker.initializer!)
214+
guard let initializingStore = walker.initializingStore else {
215+
return nil
216+
}
217+
return (initialAddress: baseAddr, initializingStore: initializingStore)
212218
}
213219
}
214220

@@ -241,15 +247,15 @@ struct AddressInitializationWalker: AddressDefUseWalker, AddressUseVisitor {
241247
var walkDownCache = WalkerCache<SmallProjectionPath>()
242248

243249
var isProjected = false
244-
var initializer: Instruction?
250+
var initializingStore: Instruction?
245251

246252
private mutating func setInitializer(instruction: Instruction) -> WalkResult {
247253
// An initializer must be unique and store the full value.
248-
if initializer != nil || isProjected {
249-
initializer = nil
254+
if initializingStore != nil || isProjected {
255+
initializingStore = nil
250256
return .abortWalk
251257
}
252-
initializer = instruction
258+
initializingStore = instruction
253259
return .continueWalk
254260
}
255261
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@ import SIL
6666
/// Value.enclosingAccess iteratively to find to AccessBase. This
6767
/// walker is useful for finding the innermost access, which may also
6868
/// be relevant for diagnostics.
69-
func gatherVariableIntroducers(for value: Value, _ context: Context) -> [Value]
69+
func gatherVariableIntroducers(for value: Value, _ context: Context)
70+
-> SingleInlineArray<Value>
7071
{
71-
var introducers: [Value] = []
72+
var introducers = SingleInlineArray<Value>()
7273
var useDefVisitor = VariableIntroducerUseDefWalker(context) {
73-
introducers.append($0)
74+
introducers.push($0)
7475
return .continueWalk
7576
}
7677
defer { useDefVisitor.deinitialize() }
@@ -1052,10 +1053,32 @@ extension LifetimeDependenceDefUseWalker {
10521053
if let conv = apply.convention(of: operand), conv.isIndirectOut {
10531054
return leafUse(of: operand)
10541055
}
1055-
10561056
if apply.isCallee(operand: operand) {
10571057
return leafUse(of: operand)
10581058
}
1059+
if let dep = apply.resultDependence(on: operand),
1060+
dep == .inherit {
1061+
// Operand is nonescapable and passed as a call argument. If the
1062+
// result inherits its lifetime, then consider any nonescapable
1063+
// result value to be a dependent use.
1064+
//
1065+
// If the lifetime dependence is scoped, then we can ignore it
1066+
// because a mark_dependence [nonescaping] represents the
1067+
// dependence.
1068+
if let result = apply.singleDirectResult, !result.type.isEscapable {
1069+
if dependentUse(of: operand, into: result) == .abortWalk {
1070+
return .abortWalk
1071+
}
1072+
}
1073+
for resultAddr in apply.indirectResultOperands
1074+
where !resultAddr.value.type.isEscapable {
1075+
if visitStoredUses(of: operand, into: resultAddr.value) == .abortWalk {
1076+
return .abortWalk
1077+
}
1078+
}
1079+
}
1080+
// Regardless of lifetime dependencies, consider the operand to be
1081+
// use for the duration of the call.
10591082
if apply is BeginApplyInst {
10601083
return scopedAddressUse(of: operand)
10611084
}

SwiftCompilerSources/Sources/SIL/Utilities/SequenceUtilities.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,34 @@ extension LazyMapSequence : CollectionLikeSequence,
117117
extension LazyFilterSequence : CollectionLikeSequence,
118118
FormattedLikeArray, CustomStringConvertible, CustomReflectable
119119
where Base: CollectionLikeSequence {}
120+
121+
//===----------------------------------------------------------------------===//
122+
// Single-Element Inline Array
123+
//===----------------------------------------------------------------------===//
124+
125+
public struct SingleInlineArray<Element>: RandomAccessCollection, FormattedLikeArray {
126+
private var singleElement: Element? = nil
127+
private var multipleElements: [Element] = []
128+
129+
public init() {}
130+
131+
public var startIndex: Int { 0 }
132+
public var endIndex: Int {
133+
singleElement == nil ? 0 : multipleElements.count + 1
134+
}
135+
136+
public subscript(_ index: Int) -> Element {
137+
if index == 0 {
138+
return singleElement!
139+
}
140+
return multipleElements[index - 1]
141+
}
142+
143+
public mutating func push(_ element: Element) {
144+
guard singleElement != nil else {
145+
singleElement = element
146+
return
147+
}
148+
multipleElements.append(element)
149+
}
150+
}

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ llvm::cl::opt<bool>
6969
EnableDeinitDevirtualizer("enable-deinit-devirtualizer", llvm::cl::init(false),
7070
llvm::cl::desc("Enable the DestroyHoisting pass."));
7171

72+
// Temporary flag until the stdlib builds with ~Escapable
73+
llvm::cl::opt<bool>
74+
EnableLifetimeDependenceInsertion(
75+
"enable-lifetime-dependence-insertion", llvm::cl::init(false),
76+
llvm::cl::desc("Enable lifetime dependence insertion."));
77+
78+
// Temporary flag until the stdlib builds with ~Escapable
7279
llvm::cl::opt<bool>
7380
EnableLifetimeDependenceDiagnostics(
7481
"enable-lifetime-dependence-diagnostics", llvm::cl::init(false),
@@ -291,7 +298,7 @@ SILPassPipelinePlan::getSILGenPassPipeline(const SILOptions &Options) {
291298
P.startPipeline("SILGen Passes");
292299

293300
P.addSILGenCleanup();
294-
if (EnableLifetimeDependenceDiagnostics) {
301+
if (EnableLifetimeDependenceDiagnostics || EnableLifetimeDependenceInsertion) {
295302
P.addLifetimeDependenceInsertion();
296303
}
297304
if (SILViewSILGenCFG) {

test/SILOptimizer/lifetime_dependence_diagnostics.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,26 @@ func bv_copy(_ bv: borrowing BV) -> _copy(bv) BV {
1818
copy bv
1919
}
2020

21-
// Diagnostics resolves mark_dependence [nonescaping].
21+
// No mark_dependence is needed for a inherited scope.
2222
//
23-
// CHECK-LABEL: sil hidden @$s4test14bv_borrow_copy0B0AA2BVVAE_tF : $@convention(thin) (@guaranteed BV) -> _scope(1) @owned BV {
24-
// CHECK: bb0(%0 : @noImplicitCopy $BV):
25-
// CHECK: [[R:%.*]] = apply %{{.*}}(%0) : $@convention(thin) (@guaranteed BV) -> _inherit(1) @owned BV
26-
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[R]] : $BV on %0 : $BV
27-
// CHECK: return [[MD]] : $BV
28-
// CHECK-LABEL: } // end sil function '$s4test14bv_borrow_copy0B0AA2BVVAE_tF'
29-
func bv_borrow_copy(bv: borrowing BV) -> _borrow(bv) BV {
23+
// CHECK-LABEL: sil hidden @$s4test14bv_borrow_copyyAA2BVVADF : $@convention(thin) (@guaranteed BV) -> _scope(1) @owned BV {
24+
// CHECK: bb0(%0 : @noImplicitCopy $BV):
25+
// CHECK: apply %{{.*}}(%0) : $@convention(thin) (@guaranteed BV) -> _inherit(1) @owned BV // user: %4
26+
// CHECK-NEXT: return %3 : $BV
27+
// CHECK-LABEL: } // end sil function '$s4test14bv_borrow_copyyAA2BVVADF'
28+
func bv_borrow_copy(_ bv: borrowing BV) -> _borrow(bv) BV {
3029
bv_copy(bv)
3130
}
31+
32+
// The mark_dependence for the borrow scope should be marked
33+
// [nonescaping] after diagnostics.
34+
//
35+
// CHECK-LABEL: sil hidden @$s4test010bv_borrow_C00B0AA2BVVAE_tF : $@convention(thin) (@guaranteed BV) -> _scope(1) @owned BV {
36+
// CHECK: bb0(%0 : @noImplicitCopy $BV):
37+
// CHECK: [[R:%.*]] = apply %{{.*}}(%0) : $@convention(thin) (@guaranteed BV) -> _scope(1) @owned BV
38+
// CHECK: %{{.*}} = mark_dependence [nonescaping] [[R]] : $BV on %0 : $BV
39+
// CHECK-NEXT: return %{{.*}} : $BV
40+
// CHECK-LABEL: } // end sil function '$s4test010bv_borrow_C00B0AA2BVVAE_tF'
41+
func bv_borrow_borrow(bv: borrowing BV) -> _borrow(bv) BV {
42+
bv_borrow_copy(bv)
43+
}

test/SILOptimizer/lifetime_dependence_inherit.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ struct NE {
3737
return self
3838
}
3939
}
40+
41+
// Test lifetime inheritance through chained consumes.
42+
//
43+
// This requires an inherit_lifetime marker on the argument.
44+
func bv_derive(bv: consuming BV) -> _consume(bv) BV {
45+
bv.derive()
46+
}

test/SILOptimizer/lifetime_dependence_inherit_fail.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,3 @@ struct NE {
3636
return self
3737
}
3838
}
39-
40-
func bv_derive_local(bv: consuming BV) -> _consume(bv) BV {
41-
let bv2 = BV(bv.p, bv.i)
42-
return bv2.derive() // expected-error {{lifetime-dependent value escapes its scope}}
43-
// expected-note @-2 {{it depends on the lifetime of variable 'bv2'}}
44-
// expected-note @-2 {{this use causes the lifetime-dependent value to escape}}
45-
}

0 commit comments

Comments
 (0)