Skip to content

Commit 8d64bc0

Browse files
committed
LifetimeDependenceDiagnostics: immortal dependence on global 'let'
1 parent b35d97a commit 8d64bc0

File tree

3 files changed

+51
-10
lines changed

3 files changed

+51
-10
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ private struct DiagnoseDependence {
200200
if function.hasUnsafeNonEscapableResult {
201201
return .continueWalk
202202
}
203+
// If the dependence scope is global, then it has immortal lifetime.
204+
if case .global = dependence.scope {
205+
return .continueWalk
206+
}
203207
// Check that the parameter dependence for this result is the same
204208
// as the current dependence scope.
205209
if let arg = dependence.scope.parentValue as? FunctionArgument,

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ struct LifetimeDependence : CustomStringConvertible {
6666
/// A local variable scope without ownership. The scope must be introduced by either move_value or
6767
/// begin_borrow. LinearLiveness computes the scope boundary.
6868
case local(VariableScopeInstruction)
69+
/// A directly accessed global variable without exclusivity. Presumably immutable and therefore immortal.
70+
case global(GlobalAddrInst)
6971
/// Singly-initialized addressable storage (likely for an immutable address-only value). The lifetime extends until
7072
/// the memory is destroyed. e.g. A value produced by an @in FunctionArgument, an @out apply, or an @inout
7173
/// FunctionArgument that is never modified inside the callee. Modified @inout FunctionArguments have caller scoped
@@ -82,6 +84,7 @@ struct LifetimeDependence : CustomStringConvertible {
8284
case let .owned(value): return value
8385
case let .borrowed(beginBorrow): return beginBorrow.value
8486
case let .local(varScope): return varScope.scopeBegin
87+
case let .global(ga): return ga
8588
case let .initialized(initializer): return initializer.initialAddress
8689
case let .unknown(value): return value
8790
}
@@ -91,7 +94,7 @@ struct LifetimeDependence : CustomStringConvertible {
9194
switch self {
9295
case let .caller(argument):
9396
precondition(argument.ownership != .owned, "only guaranteed or inout arguments have a caller scope")
94-
case .access, .local, .unknown:
97+
case .access, .local, .global, .unknown:
9598
break
9699
case let .yield(value):
97100
precondition(value.definingInstruction is BeginApplyInst)
@@ -119,6 +122,7 @@ struct LifetimeDependence : CustomStringConvertible {
119122
case .owned: return "Owned: "
120123
case .borrowed: return "Borrowed: "
121124
case .local: return "Local: "
125+
case .global: return "Global: "
122126
case .initialized: return "Initialized: "
123127
case .unknown: return "Unknown: "
124128
}
@@ -254,8 +258,15 @@ extension LifetimeDependence.Scope {
254258
case let .box(projectBox):
255259
// Note: the box may be in a borrow scope.
256260
self.init(base: projectBox.operand.value, context)
257-
case .stack, .global, .class, .tail, .pointer, .index, .unidentified:
258-
self = .unknown(accessBase.address ?? address)
261+
case .stack, .class, .tail, .pointer, .index, .unidentified:
262+
self = .unknown(accessBase.address!)
263+
case .global:
264+
// TODO: When AccessBase stores global_addr, we don't need a check here and don't need to pass 'address' in.
265+
if let ga = address as? GlobalAddrInst {
266+
self = .global(ga)
267+
} else {
268+
self = .unknown(accessBase.address!)
269+
}
259270
case let .argument(arg):
260271
if arg.convention.isIndirectIn {
261272
if arg.convention.isGuaranteed {
@@ -321,6 +332,8 @@ extension LifetimeDependence.Scope {
321332
self = .caller(arg)
322333
} else if let varScope = VariableScopeInstruction(value.definingInstruction) {
323334
self = .local(varScope)
335+
} else if let ga = value as? GlobalAddrInst {
336+
self = .global(ga)
324337
} else {
325338
self = .unknown(value)
326339
}
@@ -365,7 +378,7 @@ extension LifetimeDependence.Scope {
365378
/// Note: The caller must deinitialize the returned range.
366379
func computeRange(_ context: Context) -> InstructionRange? {
367380
switch self {
368-
case .caller:
381+
case .caller, .global:
369382
return nil
370383
case let .access(beginAccess):
371384
var range = InstructionRange(begin: beginAccess, context)

test/SILOptimizer/lifetime_dependence/semantics.swift

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,25 @@ internal func _overrideLifetime<
3939

4040
// Lifetime dependence semantics by example.
4141
public struct Span<T>: ~Escapable {
42-
private var base: UnsafePointer<T>
42+
private var base: UnsafePointer<T>?
4343
private var count: Int
4444

4545
@lifetime(borrow base)
46-
init(base: UnsafePointer<T>, count: Int) {
46+
init(base: UnsafePointer<T>?, count: Int) {
4747
self.base = base
4848
self.count = count
4949
}
5050

5151
@lifetime(borrow generic)
52-
init<S>(base: UnsafePointer<T>, count: Int, generic: borrowing S) {
52+
init<S>(base: UnsafePointer<T>?, count: Int, generic: borrowing S) {
5353
self.base = base
5454
self.count = count
5555
}
5656
}
5757

5858
extension Span {
5959
consuming func dropFirst() -> Span<T> {
60-
let nextPointer = self.base + 1
60+
let nextPointer = self.base.flatMap { $0 + 1 }
6161
let local = Span(base: nextPointer, count: self.count - 1)
6262
return _overrideLifetime(local, copying: self)
6363
}
@@ -67,8 +67,10 @@ extension Span {
6767
mutating func droppingPrefix(length: Int) -> /* */ Span<T> {
6868
let oldBase = base
6969
let result = Span(base: oldBase, count: length)
70-
self.base += length
71-
self.count -= length
70+
if let base = self.base {
71+
self.base = base + length
72+
self.count -= length
73+
}
7274
return _overrideLifetime(result, copying: self)
7375
}
7476
}
@@ -314,6 +316,28 @@ public func testTrivialInoutBorrow(p: inout UnsafePointer<Int>) -> Span<Int> {
314316
return Span(base: p, count: 1)
315317
}
316318

319+
// =============================================================================
320+
// Scoped dependence on global values
321+
// =============================================================================
322+
323+
private let immortalInt = 0
324+
325+
private let immortalString = ""
326+
327+
@lifetime(immortal)
328+
func testImmortalInt() -> Span<Int> {
329+
let nilBasedBuffer = UnsafeBufferPointer<Int>(start: nil, count: 0)
330+
let span = Span(base: nilBasedBuffer.baseAddress, count: nilBasedBuffer.count)
331+
return _overrideLifetime(span, borrowing: immortalInt)
332+
}
333+
334+
@lifetime(immortal)
335+
func testImmortalString() -> Span<String> {
336+
let nilBasedBuffer = UnsafeBufferPointer<String>(start: nil, count: 0)
337+
let span = Span(base: nilBasedBuffer.baseAddress, count: nilBasedBuffer.count)
338+
return _overrideLifetime(span, borrowing: immortalString)
339+
}
340+
317341
// =============================================================================
318342
// Scoped dependence on mutable values
319343
// =============================================================================

0 commit comments

Comments
 (0)