Skip to content

Commit d2dea32

Browse files
committed
Fix computeAddressableRange, test, and comment.
Address review feedback from the previous commit. (cherry picked from commit 712d39a)
1 parent 4f0dfc1 commit d2dea32

File tree

4 files changed

+181
-8
lines changed

4 files changed

+181
-8
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,13 @@ extension LifetimeDependence.Scope {
528528

529529
// For trivial values, compute the range in which the value may have its address taken.
530530
// Returns nil unless the address has both an addressable parameter use and a dealloc_stack use.
531+
//
532+
// This extended range is convervative. In theory, it can lead to a "false positive" diagnostic if this scope was
533+
// computed for a apply site that takes this address as *non-addressable*, as opposed to the apply site discovered
534+
// here that takes the alloc_stack as an *addressable* argument. This won't normally happen because addressability
535+
// depends on the value's type (via @_addressableForDepenencies), so all other lifetime-dependent applies should be
536+
// addressable. Furthermore, SILGen only creates temporary stack locations for addressable arguments for a single
537+
// argument.
531538
private static func computeAddressableRange(initializer: AccessBase.Initializer, _ context: Context)
532539
-> InstructionRange? {
533540
switch initializer {
@@ -538,23 +545,23 @@ extension LifetimeDependence.Scope {
538545
return nil
539546
}
540547
var isAddressable = false
541-
var deallocInst: DeallocStackInst?
548+
var deallocInsts = SingleInlineArray<DeallocStackInst>()
542549
for use in initialAddr.uses {
543550
let inst = use.instruction
544551
switch inst {
545-
case let apply as ApplyInst:
552+
case let apply as ApplySite:
546553
isAddressable = isAddressable || apply.isAddressable(operand: use)
547554
case let dealloc as DeallocStackInst:
548-
deallocInst = dealloc
555+
deallocInsts.append(dealloc)
549556
default:
550557
break
551558
}
552559
}
553-
guard let deallocInst = deallocInst, isAddressable else {
560+
guard isAddressable, !deallocInsts.isEmpty else {
554561
return nil
555562
}
556563
var range = InstructionRange(begin: initializingStore, context)
557-
range.insert(deallocInst)
564+
range.insert(contentsOf: deallocInsts)
558565
return range
559566
}
560567
}

test/SILOptimizer/lifetime_dependence/scope_fixup.sil

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ public struct AThing : ~Copyable {
8787
init(somethings: UnsafeMutableRawBufferPointer)
8888
}
8989

90+
@_addressableForDependencies
91+
public struct InlineInt {
92+
var i: Int
93+
}
94+
9095
sil @getContainer : $@convention(thin) (@inout AThing) -> @lifetime(borrow address_for_deps 0) @out Container
9196
sil @getMutableView : $@convention(thin) (@in_guaranteed Container) -> @lifetime(copy 0) @out Optional<MutableView>
9297
sil @modAThing : $@convention(thin) (@inout AThing) -> ()
@@ -138,6 +143,10 @@ sil @getAddressableObj : $@convention(method) (@guaranteed Holder) -> @owned Add
138143

139144
sil @getAddressableObj_NE : $@convention(thin) (@in_guaranteed AddressableForDepsObj) -> @owned NE
140145

146+
sil @globalAddress : $@convention(thin) () -> Builtin.RawPointer
147+
sil @addressInt : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
148+
sil @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
149+
141150
// NCContainer.wrapper._read:
142151
// var wrapper: Wrapper {
143152
// _read {
@@ -916,3 +925,50 @@ bb2:
916925
dealloc_box [dead_end] %1
917926
unreachable
918927
}
928+
929+
// Test trivial alloc_stack extension when used by an addressable dependency.
930+
//
931+
// CHECK-LABEL: sil hidden [noinline] [ossa] @testTempTrivialStackCopy : $@convention(thin) () -> () {
932+
// CHECK: [[STK:%[0-9]+]] = alloc_stack $InlineInt
933+
// CHECK: [[MD:%[0-9]+]] = mark_dependence [unresolved] %{{.*}} on [[STK]]
934+
// CHECK: [[VAR:%[0-9]+]] = move_value [var_decl] [[MD]]
935+
// CHECK: bb3: // Preds: bb2 bb1
936+
// CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[VAR]]
937+
// CHECK: apply %{{.*}}([[BORROW]]) : $@convention(thin) (@guaranteed Span<Int>) -> ()
938+
// CHECK: destroy_value [[VAR]]
939+
// CHECK: dealloc_stack [[STK]]
940+
// CHECK-LABEL: } // end sil function 'testTempTrivialStackCopy'
941+
sil hidden [noinline] [ossa] @testTempTrivialStackCopy : $@convention(thin) () -> () {
942+
bb0:
943+
%0 = function_ref @globalAddress : $@convention(thin) () -> Builtin.RawPointer
944+
%1 = apply %0() : $@convention(thin) () -> Builtin.RawPointer
945+
%2 = pointer_to_address %1 to [strict] $*InlineInt
946+
%3 = begin_access [read] [dynamic] %2
947+
%4 = load [trivial] %3
948+
end_access %3
949+
%6 = alloc_stack $InlineInt
950+
store %4 to [trivial] %6
951+
952+
%8 = function_ref @addressInt : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
953+
%9 = apply %8(%6) : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
954+
%10 = mark_dependence [unresolved] %9 on %6
955+
%11 = move_value [var_decl] %10
956+
cond_br undef, bb1, bb2
957+
958+
bb1:
959+
dealloc_stack %6
960+
br bb3
961+
bb2:
962+
dealloc_stack %6
963+
br bb3
964+
965+
bb3:
966+
%13 = begin_borrow %11
967+
968+
%14 = function_ref @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
969+
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Span<Int>) -> ()
970+
end_borrow %13
971+
destroy_value %11
972+
%18 = tuple ()
973+
return %18
974+
}

test/SILOptimizer/lifetime_dependence/verify_diagnostics.sil

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public struct InlineInt {
6767
}
6868
sil @globalAddress : $@convention(thin) () -> Builtin.RawPointer
6969
sil @addressInt : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
70+
sil @addressOfInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow address 0) @owned Span<Int>
71+
sil @noAddressInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow 0) @owned Span<Int>
7072
sil @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
7173

7274
// Test returning a owned dependence on a trivial value
@@ -259,10 +261,12 @@ bb0(%0 : $*NE, %1 : $*Holder):
259261
// extends to the dealloc_stack.
260262
//
261263
// SILGen should never generate temporary stack copies for addressable dependencies, but we need to diagnose them anyway
262-
// so that a SILGen bug does not become a miscompile.
264+
// so that a SILGen bug does not become a miscompile. Furthermore, LifetimeDependenceScopeFixup now automatically
265+
// extends such local allocations, so this diagnostic only fires on source-level tests when scope extension is
266+
// impossible.
263267
//
264268
// rdar://159680262 ([nonescapable] diagnose dependence on a temporary copy of a global array)
265-
sil hidden [noinline] [ossa] @testTrivialStackCopy : $@convention(thin) () -> () {
269+
sil hidden [noinline] [ossa] @testTempTrivialStackCopy : $@convention(thin) () -> () {
266270
bb0:
267271
%0 = function_ref @globalAddress : $@convention(thin) () -> Builtin.RawPointer
268272
%1 = apply %0() : $@convention(thin) () -> Builtin.RawPointer
@@ -287,3 +291,64 @@ bb0:
287291
%18 = tuple ()
288292
return %18
289293
}
294+
295+
// Test an address dependency on a trivial value temporarily copied to the stack. The value's addressable range only
296+
// extends to the dealloc_stack. This represents the SIL from @testTempTrivialStackCopy after
297+
// LifetimeDependenceScopeFixup has extended the local allocation.
298+
sil hidden [noinline] [ossa] @testExtendedTrivialStackCopy : $@convention(thin) () -> () {
299+
bb0:
300+
%0 = function_ref @globalAddress : $@convention(thin) () -> Builtin.RawPointer
301+
%1 = apply %0() : $@convention(thin) () -> Builtin.RawPointer
302+
%2 = pointer_to_address %1 to [strict] $*InlineInt
303+
%3 = begin_access [read] [dynamic] %2
304+
%4 = load [trivial] %3
305+
end_access %3
306+
%6 = alloc_stack $InlineInt
307+
store %4 to [trivial] %6
308+
309+
%8 = function_ref @addressInt : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
310+
%9 = apply %8(%6) : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address_for_deps 0) @owned Span<Int>
311+
%10 = mark_dependence [unresolved] %9 on %6
312+
%11 = move_value [var_decl] %10
313+
%13 = begin_borrow %11
314+
315+
%14 = function_ref @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
316+
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Span<Int>) -> ()
317+
end_borrow %13
318+
destroy_value %11
319+
dealloc_stack %6
320+
%18 = tuple ()
321+
return %18
322+
}
323+
324+
// Test two address dependencies on the same temporay. The first dependency is addressable; it's uses are covered by the
325+
// stack allocation. The second dependency is non-addressable; it's uses are not covered by the stack allocation. So,
326+
// this is correct SIL but is falsely diagnosed as a lifetime error. This conservative error is fine because SILGen
327+
// never reuses a temporary allocaiton for both an addressable and non-addressable dependency.
328+
sil hidden [noinline] [ossa] @testFalseTempTrivialStackCopy : $@convention(thin) (Int) -> () {
329+
bb0(%0: $Int):
330+
%1 = alloc_stack $Int
331+
store %0 to [trivial] %1
332+
333+
%3 = function_ref @addressOfInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow address 0) @owned Span<Int>
334+
%4 = apply %3(%1) : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow address 0) @owned Span<Int>
335+
%5 = mark_dependence [unresolved] %4 on %1
336+
%6 = move_value [var_decl] %5
337+
destroy_value %6
338+
339+
%8 = function_ref @noAddressInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow 0) @owned Span<Int>
340+
%9 = apply %8(%1) : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow 0) @owned Span<Int>
341+
%10 = mark_dependence [unresolved] %9 on %1
342+
%11 = move_value [var_decl] %10 // expected-error{{lifetime-dependent value escapes its scope}}
343+
// expected-note@-15{{it depends on the lifetime of an argument of 'testFalseTempTrivialStackCopy'}}
344+
345+
dealloc_stack %1
346+
347+
%13 = begin_borrow %11
348+
%14 = function_ref @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
349+
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Span<Int>) -> ()
350+
end_borrow %13
351+
destroy_value %11 // expected-note{{this use of the lifetime-dependent value is out of scope}}
352+
%18 = tuple ()
353+
return %18
354+
}

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,18 +324,63 @@ func dependOnNonCopyable() -> NCBuffer {
324324
@available(Span 0.1, *)
325325
var values: InlineArray<_, Int> = [0, 1, 2]
326326

327+
@available(Span 0.1, *)
328+
func getValues() -> InlineArray<3, Int> { values }
329+
330+
// Test a trivial global variable used by addressableForDependencies (InlineArray.span). This either requires a direct address
331+
// to the global or it requires extension of the local stack allocation by LifetimeDependenceScopeFixup.
332+
//
327333
// rdar://159680262 ([nonescapable] diagnose dependence on a temporary copy of a global array)
328334
@available(Span 0.1, *)
329335
func readGlobalSpan() -> Int {
330336
let span = values.span
331337
return span[3]
332338
}
333339

334-
// TODO: rdar://151320168 ([nonescapable] support immortal span over global array)
340+
// Test a trivial global variable used by addressableForDependencies (InlineArray.span).
341+
//
342+
// TODO: This will no longer be an error when SILGen passes a direct address to the global instead of a temporary stack
343+
// allocation: rdar://151320168 ([nonescapable] support immortal span over global array)
335344
@available(Span 0.1, *)
336345
@_lifetime(immortal)
337346
func returnGlobalSpan() -> Span<Int> {
338347
let span = values.span // expected-error{{lifetime-dependent variable 'span' escapes its scope}}
339348
// expected-note@-1{{it depends on this scoped access to variable 'values'}}
340349
return span // expected-note{{this use causes the lifetime-dependent value to escape}}
341350
}
351+
352+
// Test a trivial temporary stack allocation used by addressableForDependencies
353+
// (InlineArray.span). LifetimeDependenceScopeFixup needs to extend the local stack allocation.
354+
@available(Span 0.1, *)
355+
func readTempSpan() -> Int {
356+
let span = getValues().span
357+
return span[3]
358+
}
359+
360+
// Test a trivial temporary stack allocation used by addressableForDependencies (InlineArray.span). This illegally
361+
// returns an address into a temporary.
362+
@available(Span 0.1, *)
363+
@_lifetime(immortal)
364+
func returnTempSpan() -> Span<Int> {
365+
let span = getValues().span // expected-error{{lifetime-dependent variable 'span' escapes its scope}}
366+
// expected-note@-1{{it depends on the lifetime of this parent value}}
367+
return span // expected-note{{this use causes the lifetime-dependent value to escape}}
368+
}
369+
370+
// Test a trivial temporary stack allocation used by an addressable parameter
371+
// (Borrow.init). LifetimeDependenceScopeFixup needs to extend the local stack allocation.
372+
@available(Span 0.1, *)
373+
func readTempBorrow() -> Int {
374+
let borrow = Borrow(3)
375+
return borrow[]
376+
}
377+
378+
// Test a trivial temporary stack allocation used by an addressable parameter (Borrow.init). This illegally returns an
379+
// address into a temporary.
380+
@available(Span 0.1, *)
381+
@_lifetime(immortal)
382+
func returnTempBorrow() -> Borrow<Int> {
383+
let span = Borrow(3) // expected-error{{lifetime-dependent variable 'span' escapes its scope}}
384+
// expected-note@-1{{it depends on the lifetime of this parent value}}
385+
return span // expected-note{{this use causes the lifetime-dependent value to escape}}
386+
}

0 commit comments

Comments
 (0)