Skip to content

Commit d03fd67

Browse files
authored
Merge pull request #82797 from atrick/62-local-deadend
[6.2] Improve LocalVariableUtils.gatherKnownLifetimeUses; dead ends
2 parents 8e96e1a + 9f7bf2a commit d03fd67

File tree

6 files changed

+141
-12
lines changed

6 files changed

+141
-12
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -648,12 +648,10 @@ extension AddressOwnershipLiveRange {
648648
let addressOwnershipLiveRangeTest = FunctionTest("address_ownership_live_range") {
649649
function, arguments, context in
650650
let address = arguments.takeValue()
651+
let begin = arguments.takeInstruction()
651652
print("Address: \(address)")
652653
print("Base: \(address.accessBase)")
653-
let begin = address.definingInstructionOrTerminator ?? {
654-
assert(address is FunctionArgument)
655-
return function.instructions.first!
656-
}()
654+
print("Begin: \(begin)")
657655
let localReachabilityCache = LocalVariableReachabilityCache()
658656
guard var ownershipRange = AddressOwnershipLiveRange.compute(for: address, at: begin,
659657
localReachabilityCache, context) else {

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,8 @@ extension LifetimeDependenceDefUseWalker {
975975
return yieldedDependence(result: localAccess.operand!)
976976
case .incomingArgument:
977977
fatalError("Incoming arguments are never reachable")
978+
case .deadEnd:
979+
return .continueWalk
978980
}
979981
}
980982

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct LocalVariableAccess: CustomStringConvertible {
7272
case storeBorrow // scoped initialization of temporaries
7373
case apply // indirect arguments
7474
case escape // alloc_box captures
75+
case deadEnd // unreachable
7576
}
7677
let kind: Kind
7778
// All access have an operand except .incomingArgument and .outgoingArgument.
@@ -102,7 +103,7 @@ struct LocalVariableAccess: CustomStringConvertible {
102103
case .`init`, .modify:
103104
return true
104105
}
105-
case .load, .dependenceSource, .dependenceDest:
106+
case .load, .dependenceSource, .dependenceDest, .deadEnd:
106107
return false
107108
case .incomingArgument, .outgoingArgument, .store, .storeBorrow, .inoutYield:
108109
return true
@@ -153,6 +154,8 @@ struct LocalVariableAccess: CustomStringConvertible {
153154
str += "apply"
154155
case .escape:
155156
str += "escape"
157+
case .deadEnd:
158+
str += "deadEnd"
156159
}
157160
if let inst = instruction {
158161
str += "\(inst)"
@@ -201,7 +204,7 @@ class LocalVariableAccessInfo: CustomStringConvertible {
201204
self.hasEscaped = true
202205
case .inoutYield:
203206
self._isFullyAssigned = false
204-
case .incomingArgument, .outgoingArgument:
207+
case .incomingArgument, .outgoingArgument, .deadEnd:
205208
fatalError("Function arguments are never mapped to LocalVariableAccessInfo")
206209
}
207210
}
@@ -753,8 +756,8 @@ extension LocalVariableReachableAccess {
753756
///
754757
/// This does not include the destroy or reassignment of the value set by `modifyInst`.
755758
///
756-
/// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` are
757-
/// reachable from `modifyInst`.
759+
/// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` or
760+
/// are reachable from `modifyInst`.
758761
///
759762
/// This does not gather the escaping accesses themselves. When escapes are reachable, it also does not guarantee that
760763
/// previously reachable accesses are gathered.
@@ -855,6 +858,8 @@ extension LocalVariableReachableAccess {
855858
}
856859
if block.terminator.isFunctionExiting {
857860
accessStack.push(LocalVariableAccess(.outgoingArgument, block.terminator))
861+
} else if block.successors.isEmpty {
862+
accessStack.push(LocalVariableAccess(.deadEnd, block.terminator))
858863
} else {
859864
for successor in block.successors { blockList.pushIfNotVisited(successor) }
860865
}

test/SILOptimizer/lifetime_dependence/scope_fixup.sil

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ class C {
119119

120120
sil @C_read : $@yield_once @convention(method) (@guaranteed C) -> @yields @guaranteed Optional<NCWrapper>
121121

122+
struct S {
123+
let c: C
124+
}
125+
struct MutNE: ~Copyable & ~Escapable {}
126+
127+
sil @getMutNE : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
128+
122129
// NCContainer.wrapper._read:
123130
// var wrapper: Wrapper {
124131
// _read {
@@ -791,3 +798,54 @@ bb2:
791798
%42 = tuple ()
792799
return %42
793800
}
801+
802+
// rdar://154406790 (Lifetime-dependent variable 'X' escapes its scope but only if actor/class is final)
803+
//
804+
// The end_access must be extended into the unreachable block past the end_borrow.
805+
//
806+
// CHECK-LABEL: sil hidden [ossa] @testInoutAtModify : $@convention(thin) (@inout S) -> () {
807+
// CHECK: bb0(%0 : $*S):
808+
// CHECK: [[ACCESS:%[0-9]+]] = begin_access [modify] [unknown] %0
809+
// CHECK: apply %{{.*}}([[ACCESS]]) : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
810+
// CHECK: mark_dependence [unresolved] %{{.*}} on [[ACCESS]]
811+
// CHECK: [[LB:%[0-9]+]] = load_borrow
812+
//
813+
// CHECK: bb1:
814+
// CHECK: end_borrow [[LB]]
815+
// CHECK: end_access [[ACCESS]]
816+
// CHECK: return %16
817+
//
818+
// CHECK: bb2:
819+
// CHECK: end_borrow [[LB]]
820+
// CHECK: end_access [[ACCESS]]
821+
// CHECK: unreachable
822+
// CHECK-LABEL: } // end sil function 'testInoutAtModify'
823+
sil hidden [ossa] @testInoutAtModify : $@convention(thin) (@inout S) -> () {
824+
bb0(%0 : $*S):
825+
%1 = alloc_box ${ let MutNE }, let, name "ne"
826+
%2 = begin_borrow [lexical] [var_decl] %1
827+
%3 = project_box %2, 0
828+
%4 = begin_access [modify] [unknown] %0
829+
830+
%6 = function_ref @getMutNE : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
831+
%7 = apply %6(%4) : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
832+
%8 = mark_dependence [unresolved] %7 on %4
833+
store %8 to [init] %3
834+
end_access %4
835+
%11 = mark_unresolved_non_copyable_value [no_consume_or_assign] %3
836+
%12 = load_borrow %11
837+
cond_br undef, bb1, bb2
838+
839+
bb1:
840+
end_borrow %12
841+
end_borrow %2
842+
destroy_value %1
843+
%15 = tuple ()
844+
return %15
845+
846+
bb2:
847+
end_borrow %12
848+
end_borrow %2
849+
dealloc_box [dead_end] %1
850+
unreachable
851+
}

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,16 @@ func testSwitchAddr<T>(holder: inout Holder, t: T) {
274274
mutate(&holder) // expected-note {{conflicting access is here}}
275275
mutableView.modify()
276276
}
277+
278+
// =============================================================================
279+
// Throwing
280+
// =============================================================================
281+
282+
@available(Span 0.1, *)
283+
func mutableSpanMayThrow(_: borrowing MutableSpan<Int>) throws {}
284+
285+
@available(Span 0.1, *)
286+
func testSpanMayThrow(buffer: inout [Int]) {
287+
let bufferSpan = buffer.mutableSpan
288+
try! mutableSpanMayThrow(bufferSpan)
289+
}
Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
1-
// RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -test-runner \
2+
// RUN: -enable-experimental-feature Lifetimes \
3+
// RUN: %s -o /dev/null 2>&1 | %FileCheck %s
24

3-
sil_stage canonical
5+
// REQUIRES: swift_feature_Lifetimes
6+
7+
sil_stage raw
48

59
import Builtin
10+
import Swift
611

712
class C {}
813
class D {
914
var object: C
1015
}
1116

17+
struct S {
18+
let c: C
19+
}
20+
21+
struct MutNE: ~Copyable & ~Escapable {}
22+
23+
sil @getMutNE : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
24+
1225
// An address live range can be extended by a dependent value.
1326
//
1427
// CHECK-LABEL: testMarkDepAddressBase: address_ownership_live_range with: %f0
1528
// CHECK-NEXT: Address: [[F0:%.*]] = ref_element_addr %0 : $D, #D.object
1629
// CHECK-NEXT: Base: class - [[F0]] = ref_element_addr %0 : $D, #D.object
17-
// CHECK-NEXT: borrow: functionArgument(%0 = argument of bb0 : $D
30+
// CHECK: borrow: functionArgument(%0 = argument of bb0 : $D
1831
// CHECK-NEXT: begin: [[F0]] = ref_element_addr %0 : $D, #D.object
1932
// CHECK-NEXT: ends: end_borrow %{{.*}} : $C
2033
// CHECK-NEXT: exits:
@@ -26,10 +39,50 @@ sil [ossa] @testMarkDepAddressBase : $@convention(thin) (@guaranteed D, @guarant
2639
bb0(%0 : @guaranteed $D, %1 : @guaranteed $D):
2740
%f0 = ref_element_addr %0 : $D, #D.object
2841
%f1 = ref_element_addr %1 : $D, #D.object
29-
specify_test "address_ownership_live_range %f0"
42+
specify_test "address_ownership_live_range %f0 %f0"
3043
%dependence = mark_dependence %f1 on %f0
3144
%load = load_borrow %dependence
3245
end_borrow %load
3346
%99 = tuple()
3447
return %99 : $()
3548
}
49+
50+
// CHECK-LABEL: testInoutAtModify: address_ownership_live_range
51+
// CHECK: Address: %0 = argument of bb0 : $*S
52+
// CHECK: Begin: [[ACCESS:%[0-9]+]] = begin_access [modify] [unknown] %0 : $*S
53+
// CHECK: local: %0 = argument of bb0 : $*S
54+
// CHECK: begin: %{{.*}} = alloc_box ${ let MutNE }, let, name "ne"
55+
// CHECK: ends: unreachable
56+
// CHECK: return
57+
// CHECK: exits:
58+
// CHECK: interiors: end_access
59+
// CHECK-LABEL: testInoutAtModify: address_ownership_live_range
60+
sil hidden [ossa] @testInoutAtModify : $@convention(thin) (@inout S) -> () {
61+
bb0(%0 : $*S):
62+
%1 = alloc_box ${ let MutNE }, let, name "ne"
63+
%2 = begin_borrow [lexical] [var_decl] %1
64+
%3 = project_box %2, 0
65+
%4 = begin_access [modify] [unknown] %0
66+
specify_test "address_ownership_live_range %0 %4"
67+
68+
%6 = function_ref @getMutNE : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
69+
%7 = apply %6(%4) : $@convention(thin) (@inout S) -> @lifetime(borrow 0) @owned MutNE
70+
store %7 to [init] %3
71+
end_access %4
72+
%10 = mark_unresolved_non_copyable_value [no_consume_or_assign] %3
73+
%11 = load_borrow %10
74+
cond_br undef, bb1, bb2
75+
76+
bb1:
77+
end_borrow %11
78+
end_borrow %2
79+
destroy_value %1
80+
%15 = tuple ()
81+
return %15
82+
83+
bb2:
84+
end_borrow %11
85+
end_borrow %2
86+
dealloc_box [dead_end] %1
87+
unreachable
88+
}

0 commit comments

Comments
 (0)