Skip to content

Commit 8421608

Browse files
committed
LifetimeDependenceInsertion: remove a bailout on ~Copyable
Needed to diagnose MutableSpan and OutputSpan. For now, simply remove the bailout and TODO. The next change will introduce more logic to force a diagnostic error in rare cases that can't be handled completely.
1 parent 6c5ee1c commit 8421608

File tree

5 files changed

+99
-15
lines changed

5 files changed

+99
-15
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
235235
guard var sources = apply.getResultDependenceSources() else {
236236
return
237237
}
238-
log("Creating dependencies for \(apply.applySite)")
238+
log("Creating result dependencies for \(apply.applySite)")
239239

240240
// Find the dependence base for each source.
241241
sources.initializeBases(context)
@@ -249,12 +249,6 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
249249
guard case let .store(initializingStore, initialAddress) = accessBase.findSingleInitializer(context) else {
250250
continue
251251
}
252-
// TODO: This might bail-out on SIL that should be diagnosed. We should handle/cleanup projections and casts that
253-
// occur before the initializingStore. Or check in the SIL verifier that all stores without an access scope follow
254-
// this form. Then convert this bail-out to an assert.
255-
guard initialAddress.usesOccurOnOrAfter(instruction: initializingStore, context) else {
256-
continue
257-
}
258252
assert(initializingStore == resultOper.instruction, "an indirect result is a store")
259253
Builder.insert(after: apply.applySite, context) { builder in
260254
insertMarkDependencies(value: initialAddress, initializer: initializingStore, bases: sources.bases,
@@ -268,7 +262,7 @@ private func insertParameterDependencies(apply: LifetimeDependentApply, target:
268262
guard var sources = apply.getParameterDependenceSources(target: target) else {
269263
return
270264
}
271-
log("Creating dependencies for \(apply.applySite)")
265+
log("Creating parameter dependencies for \(apply.applySite)")
272266

273267
sources.initializeBases(context)
274268

@@ -285,8 +279,13 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
285279
let markDep = builder.createMarkDependence(
286280
value: currentValue, base: base, kind: .Unresolved)
287281

288-
// Address dependencies cannot be represented as SSA values, so it doesn not make sense to replace any uses of the
289-
// dependent address. TODO: consider a separate mark_dependence_addr instruction since the semantics are different.
282+
// Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the
283+
// dependent address.
284+
//
285+
// TODO: either (1) insert a separate mark_dependence_addr instruction with no return value, or (2) perform data
286+
// flow to replace all reachable address uses, and if any aren't dominated by base, then insert an extra
287+
// escaping mark_dependence at this apply site that directly uses the mark_dependence [nonescaping] to force
288+
// diagnostics to fail.
290289
if !value.type.isAddress {
291290
let uses = currentValue.uses.lazy.filter {
292291
if $0.isScopeEndingUse {

test/SILOptimizer/Inputs/SpanExtras.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public func _overrideLifetime<
2828

2929
@_unsafeNonescapableResult
3030
@inlinable @inline(__always)
31-
@lifetime(source)
31+
@lifetime(borrow source)
3232
public func _overrideLifetime<
3333
T: ~Copyable & ~Escapable,
3434
U: ~Copyable & ~Escapable
@@ -117,7 +117,7 @@ extension MutableSpan {
117117
) {
118118
let rb = UnsafeMutableBufferPointer(rebasing: elements)
119119
let ms = MutableSpan(_unsafeElements: rb)
120-
self = _overrideLifetime(of: ms, to: rb)
120+
self = _overrideLifetime(of: ms, to: elements)
121121
}
122122
}
123123

test/SILOptimizer/lifetime_dependence/dependence_insertion.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,16 @@ struct NE: ~Escapable {
1818
init()
1919
}
2020

21+
struct NCE: ~Escapable, ~Copyable {
22+
var p: UnsafeRawPointer
23+
24+
@lifetime(immortal)
25+
init()
26+
}
27+
2128
sil @getPtr : $@convention(thin) () -> @out UnsafeRawPointer
2229
sil @getSpan : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
30+
sil @getInoutSpan : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
2331

2432
// Check that the inserted dependence is on the 'self' argument, not the temporary borrow.
2533
//
@@ -62,3 +70,25 @@ bb0(%0 : @guaranteed $AnyObject):
6270
destroy_value %3
6371
return %15
6472
}
73+
74+
// CHECK-LABEL: sil [available 9999] [ossa] @testInoutSpanProp : $@convention(method) (@inout AnyObject) -> @lifetime(borrow 0) @owned NCE {
75+
// CHECK: bb0(%0 : $*AnyObject):
76+
// CHECK: [[ALLOC:%.*]] = alloc_stack $NCE
77+
// CHECK: [[MD:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[ALLOC]]
78+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0
79+
// CHECK: apply %{{.*}}([[MD]], [[ACCESS]]) : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
80+
// CHECK: mark_dependence [unresolved] [[ALLOC]] on [[ACCESS]]
81+
// CHECK: end_access [[ACCESS]]
82+
// CHECK-LABEL: } // end sil function 'testInoutSpanProp'
83+
sil [available 9999] [ossa] @testInoutSpanProp : $@convention(method) (@inout AnyObject) -> @lifetime(borrow 0) @owned NCE {
84+
bb0(%0 : $*AnyObject):
85+
%1 = alloc_stack $NCE
86+
%2 = mark_unresolved_non_copyable_value [consumable_and_assignable] %1
87+
%3 = begin_access [modify] [unknown] %0
88+
%4 = function_ref @getInoutSpan : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
89+
%5 = apply %4(%2, %3) : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
90+
end_access %3
91+
%7 = load [take] %2
92+
dealloc_stack %1
93+
return %7
94+
}

test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,54 @@
22
// RUN: -o /dev/null \
33
// RUN: -verify \
44
// RUN: -sil-verify-all \
5-
// RUN: -disable-access-control \
65
// RUN: -module-name test \
76
// RUN: -enable-experimental-feature LifetimeDependence
87

98
// REQUIRES: swift_in_compiler
109
// REQUIRES: swift_feature_LifetimeDependence
1110

11+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
12+
/// a value identical to `dependent` with a lifetime dependency on the caller's
13+
/// borrow scope of the `source` argument.
14+
@_unsafeNonescapableResult
15+
@_transparent
16+
@lifetime(borrow source)
17+
internal func _overrideLifetime<
18+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
19+
>(
20+
_ dependent: consuming T, borrowing source: borrowing U
21+
) -> T {
22+
dependent
23+
}
24+
25+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
26+
/// a value identical to `dependent` that inherits all lifetime dependencies from
27+
/// the `source` argument.
28+
@_unsafeNonescapableResult
29+
@_transparent
30+
@lifetime(source)
31+
internal func _overrideLifetime<
32+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
33+
>(
34+
_ dependent: consuming T, copying source: borrowing U
35+
) -> T {
36+
dependent
37+
}
38+
39+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
40+
/// a value identical to `dependent` that inherits all lifetime dependencies from
41+
/// the `source` argument.
42+
@_unsafeNonescapableResult
43+
@_transparent
44+
@lifetime(borrow source)
45+
internal func _overrideLifetime<
46+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
47+
>(
48+
_ dependent: consuming T, mutating source: inout U
49+
) -> T {
50+
dependent
51+
}
52+
1253
struct MutableSpan : ~Escapable, ~Copyable {
1354
let base: UnsafeMutableRawPointer
1455
let count: Int
@@ -59,7 +100,7 @@ extension Array where Element == Int {
59100
/* not the real implementation */
60101
let p = self.withUnsafeMutableBufferPointer { $0.baseAddress! }
61102
let span = MutableSpan(p, count)
62-
return _overrideLifetime(span, borrowing: self)
103+
return _overrideLifetime(span, mutating: &self)
63104
}
64105
}
65106

test/SILOptimizer/lifetime_dependence/semantics.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ internal func _overrideLifetime<
3737
dependent
3838
}
3939

40+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
41+
/// a value identical to `dependent` that inherits all lifetime dependencies from
42+
/// the `source` argument.
43+
@_unsafeNonescapableResult
44+
@_transparent
45+
@lifetime(borrow source)
46+
internal func _overrideLifetime<
47+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
48+
>(
49+
_ dependent: consuming T, mutating source: inout U
50+
) -> T {
51+
dependent
52+
}
53+
4054
// Lifetime dependence semantics by example.
4155
public struct Span<T>: ~Escapable {
4256
private var base: UnsafePointer<T>?
@@ -102,7 +116,7 @@ extension Array {
102116
/* not the real implementation */
103117
let p = self.withUnsafeMutableBufferPointer { $0.baseAddress! }
104118
let span = MutableSpan<Element>(base: p, count: 1)
105-
return _overrideLifetime(span, borrowing: self)
119+
return _overrideLifetime(span, mutating: &self)
106120
}
107121
}
108122

0 commit comments

Comments
 (0)