Skip to content

Commit c8609c7

Browse files
authored
Merge pull request #80755 from atrick/62-localvar-dataflow
[6.2] LocalVariableUtils: fix data flow propagation of escapes. #80754
2 parents c5b9a37 + e01a167 commit c8609c7

File tree

3 files changed

+194
-11
lines changed

3 files changed

+194
-11
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

+58-11
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import SIL
2727

2828
private let verbose = false
2929

30-
private func log(_ message: @autoclosure () -> String) {
30+
private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
3131
if verbose {
32-
print("### \(message())")
32+
debugLog(prefix: prefix, message())
3333
}
3434
}
3535

@@ -226,7 +226,7 @@ class LocalVariableAccessInfo: CustomStringConvertible {
226226
}
227227

228228
var description: String {
229-
return "full-assign: \(_isFullyAssigned == nil ? "unknown" : String(describing: _isFullyAssigned!)), "
229+
return "assign: \(_isFullyAssigned == nil ? "unknown" : String(describing: _isFullyAssigned!)), "
230230
+ "\(access)"
231231
}
232232

@@ -329,7 +329,7 @@ struct LocalVariableAccessMap: Collection, CustomStringConvertible {
329329
subscript(instruction: Instruction) -> LocalVariableAccessInfo? { accessMap[instruction] }
330330

331331
var description: String {
332-
"Access map:\n" + map({String(describing: $0)}).joined(separator: "\n")
332+
"Access map for: \(allocation)\n" + map({String(describing: $0)}).joined(separator: "\n")
333333
}
334334
}
335335

@@ -699,6 +699,7 @@ extension LocalVariableReachableAccess {
699699
case .escape:
700700
break
701701
}
702+
break
702703
}
703704
return currentEffect
704705
}
@@ -808,8 +809,8 @@ extension LocalVariableReachableAccess {
808809
forwardPropagateEffect(in: block, blockInfo: blockInfo, effect: currentEffect, blockList: &blockList,
809810
accessStack: &accessStack)
810811
}
811-
log("\(accessMap)")
812-
log("Reachable access:\n\(accessStack.map({ String(describing: $0)}).joined(separator: "\n"))")
812+
log("\n\(accessMap)")
813+
log(prefix: false, "Reachable access:\n\(accessStack.map({ String(describing: $0)}).joined(separator: "\n"))")
813814
return true
814815
}
815816

@@ -873,7 +874,7 @@ extension LocalVariableReachableAccess {
873874
private func findAllEscapesPriorToAccess() {
874875
var visitedBlocks = BasicBlockSet(context)
875876
var escapedBlocks = BasicBlockSet(context)
876-
var blockList = BasicBlockWorklist(context)
877+
var blockList = Stack<BasicBlock>(context)
877878
defer {
878879
visitedBlocks.deinitialize()
879880
escapedBlocks.deinitialize()
@@ -886,19 +887,19 @@ extension LocalVariableReachableAccess {
886887
for successor in from.successors {
887888
if hasEscaped {
888889
if escapedBlocks.insert(successor) {
889-
blockList.pushIfNotVisited(successor)
890+
blockList.push(successor)
890891
}
891892
} else if visitedBlocks.insert(successor) {
892-
blockList.pushIfNotVisited(successor)
893+
blockList.push(successor)
893894
}
894895
}
895896
}
896897
var hasEscaped = propagateEscapeInBlock(after: accessMap.allocation.nextInstruction, hasEscaped: false)
897898
forwardPropagate(accessMap.allocation.parentBlock, hasEscaped)
898899
while let block = blockList.pop() {
899-
hasEscaped = escapedBlocks.insert(block)
900+
hasEscaped = escapedBlocks.contains(block)
900901
hasEscaped = propagateEscapeInBlock(after: block.instructions.first!, hasEscaped: hasEscaped)
901-
forwardPropagate(accessMap.allocation.parentBlock, hasEscaped)
902+
forwardPropagate(block, hasEscaped)
902903
}
903904
}
904905

@@ -917,3 +918,49 @@ extension LocalVariableReachableAccess {
917918
return hasEscaped
918919
}
919920
}
921+
922+
let localVariableReachingAssignmentsTest = FunctionTest("local_variable_reaching_assignments") {
923+
function, arguments, context in
924+
let allocation = arguments.takeValue()
925+
let instruction = arguments.takeInstruction()
926+
print("### Allocation: \(allocation)")
927+
let localReachabilityCache = LocalVariableReachabilityCache()
928+
guard let localReachability = localReachabilityCache.reachability(for: allocation, context) else {
929+
print("No reachability")
930+
return
931+
}
932+
print("### Access map:")
933+
print(localReachability.accessMap)
934+
print("### Instruction: \(instruction)")
935+
var reachingAssignments = Stack<LocalVariableAccess>(context)
936+
defer { reachingAssignments.deinitialize() }
937+
guard localReachability.gatherReachingAssignments(for: instruction, in: &reachingAssignments) else {
938+
print("!!! Reaching escape")
939+
return
940+
}
941+
print("### Reachable assignments:")
942+
print(reachingAssignments.map({ String(describing: $0)}).joined(separator: "\n"))
943+
}
944+
945+
let localVariableReachableUsesTest = FunctionTest("local_variable_reachable_uses") {
946+
function, arguments, context in
947+
let allocation = arguments.takeValue()
948+
let modify = arguments.takeInstruction()
949+
print("### Allocation: \(allocation)")
950+
let localReachabilityCache = LocalVariableReachabilityCache()
951+
guard let localReachability = localReachabilityCache.reachability(for: allocation, context) else {
952+
print("No reachability")
953+
return
954+
}
955+
print("### Access map:")
956+
print(localReachability.accessMap)
957+
print("### Modify: \(modify)")
958+
var reachableUses = Stack<LocalVariableAccess>(context)
959+
defer { reachableUses.deinitialize() }
960+
guard localReachability.gatherAllReachableUses(of: modify, in: &reachableUses) else {
961+
print("!!! Reachable escape")
962+
return
963+
}
964+
print("### Reachable access:")
965+
print(reachableUses.map({ String(describing: $0)}).joined(separator: "\n"))
966+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ public func registerOptimizerTests() {
164164
lifetimeDependenceScopeTest,
165165
lifetimeDependenceUseTest,
166166
linearLivenessTest,
167+
localVariableReachableUsesTest,
168+
localVariableReachingAssignmentsTest,
167169
parseTestSpecificationTest,
168170
variableIntroducerTest,
169171
gatherCallSitesTest,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// RUN: %target-sil-opt -test-runner %s \
2+
// RUN: -enable-experimental-feature LifetimeDependence \
3+
// RUN: -o /dev/null 2>&1 | %FileCheck %s
4+
5+
// REQUIRES: swift_in_compiler
6+
// REQUIRES: swift_feature_LifetimeDependence
7+
8+
sil_stage raw
9+
10+
import Builtin
11+
import Swift
12+
import SwiftShims
13+
14+
struct NE: ~Escapable {}
15+
16+
sil @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE
17+
18+
// CHECK-LABEL: testNEInitNoEscape: local_variable_reachable_uses with: %1, @instruction
19+
// CHECK: ### Access map:
20+
// CHECK-NEXT: Access map for: %{{.*}} = alloc_box ${ var NE }, var, name "self"
21+
// CHECK-NEXT: assign: true, store destroy_value %{{.*}} : ${ var NE }
22+
// CHECK-NEXT: assign: false, escape %{{.*}} = address_to_pointer %{{.*}} : $*NE to $Builtin.RawPointer
23+
// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE
24+
// CHECK-NEXT: assign: false, load %{{.*}} = load [copy] %{{.*}} : $*NE
25+
// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE
26+
// CHECK-NEXT: ### Modify: %{{.*}} = begin_access [modify] [unknown] %4 : $*NE
27+
// CHECK-NEXT: ### Reachable access:
28+
// CHECK-NEXT: load %{{.*}} = load [copy] %{{.*}} : $*NE
29+
// CHECK-NEXT: testNEInitNoEscape: local_variable_reachable_uses with: %1, @instruction
30+
31+
// CHECK-LABEL: testNEInitNoEscape: local_variable_reaching_assignments with: %1, @instruction
32+
// CHECK: ### Instruction: end_access %{{.*}} : $*NE
33+
// CHECK-NEXT: ### Reachable assignments:
34+
// CHECK-NEXT: beginAccess %21 = begin_access [modify] [unknown] %4 : $*NE
35+
// CHECK-NEXT: testNEInitNoEscape: local_variable_reaching_assignments with: %1, @instruction
36+
sil [ossa] @testNEInitNoEscape : $@convention(thin) (@inout NE) -> @lifetime(borrow 0) @owned NE {
37+
bb0(%0 : $*NE):
38+
%1 = alloc_box ${ var NE }, var, name "self"
39+
%2 = mark_uninitialized [rootself] %1
40+
%3 = begin_borrow [lexical] [var_decl] %2
41+
%4 = project_box %3, 0
42+
cond_br undef, bb1, bb2
43+
44+
bb1:
45+
br bb3
46+
47+
bb2:
48+
br bb3
49+
50+
bb3:
51+
%8 = alloc_stack $NE
52+
53+
%9 = begin_access [modify] [unknown] %0
54+
55+
%10 = function_ref @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE
56+
%11 = apply %10(%8, %9) : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE
57+
58+
mark_dependence_addr [unresolved] %8 on %9
59+
end_access %9
60+
61+
%14 = load [take] %8
62+
63+
specify_test "local_variable_reachable_uses %1 @instruction"
64+
%15 = begin_access [modify] [unknown] %4
65+
assign %14 to %15
66+
end_access %15
67+
dealloc_stack %8
68+
%19 = load [copy] %4
69+
70+
%21 = load [take] %8
71+
%22 = begin_access [modify] [unknown] %4
72+
assign %21 to %22
73+
specify_test "local_variable_reaching_assignments %1 @instruction"
74+
end_access %22
75+
%escape = address_to_pointer %4 : $*NE to $Builtin.RawPointer
76+
end_borrow %3
77+
destroy_value %2
78+
return %19
79+
}
80+
81+
// CHECK-LABEL: testNEInitEscape: local_variable_reaching_assignments with: %1, @instruction
82+
// CHECK: ### Instruction: %{{.*}} = load [take] %{{.*}} : $*NE
83+
// CHECK-NEXT: !!! Reaching escape
84+
// CHECK-NEXT: testNEInitEscape: local_variable_reaching_assignments with: %1, @instruction
85+
86+
// CHECK-LABEL: testNEInitEscape: local_variable_reachable_uses with: %1, @instruction
87+
// CHECK: ### Access map:
88+
// CHECK-NEXT: Access map for: %{{.*}} = alloc_box ${ var NE }, var, name "self"
89+
// CHECK-NEXT: assign: true, store destroy_value %{{.*}} : ${ var NE }
90+
// CHECK-NEXT: assign: false, load %{{.*}} = load [copy] %{{.*}} : $*NE
91+
// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE
92+
// CHECK-NEXT: assign: false, escape %6 = address_to_pointer %4 : $*NE to $Builtin.RawPointer
93+
// CHECK-NEXT: ### Modify: %{{.*}} = begin_access [modify] [unknown] %4 : $*NE
94+
// CHECK-NEXT: !!! Reachable escape
95+
// CHECK-NEXT: testNEInitEscape: local_variable_reachable_uses with: %1, @instruction
96+
sil [ossa] @testNEInitEscape : $@convention(thin) (@inout NE) -> @lifetime(borrow 0) @owned NE {
97+
bb0(%0 : $*NE):
98+
%1 = alloc_box ${ var NE }, var, name "self"
99+
%2 = mark_uninitialized [rootself] %1
100+
%3 = begin_borrow [lexical] [var_decl] %2
101+
%4 = project_box %3, 0
102+
cond_br undef, bb1, bb2
103+
104+
bb1:
105+
%escape = address_to_pointer %4 : $*NE to $Builtin.RawPointer
106+
br bb3
107+
108+
bb2:
109+
br bb3
110+
111+
bb3:
112+
%8 = alloc_stack $NE
113+
114+
%9 = begin_access [modify] [unknown] %0
115+
116+
%10 = function_ref @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE
117+
%11 = apply %10(%8, %9) : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE
118+
119+
mark_dependence_addr [unresolved] %8 on %9
120+
end_access %9
121+
122+
specify_test "local_variable_reaching_assignments %1 @instruction"
123+
%14 = load [take] %8
124+
125+
specify_test "local_variable_reachable_uses %1 @instruction"
126+
%15 = begin_access [modify] [unknown] %4
127+
assign %14 to %15
128+
end_access %15
129+
dealloc_stack %8
130+
%19 = load [copy] %4
131+
end_borrow %3
132+
destroy_value %2
133+
return %19
134+
}

0 commit comments

Comments
 (0)