@@ -34,54 +34,26 @@ import SIL
34
34
let releaseDevirtualizerPass = FunctionPass ( name: " release-devirtualizer " ) {
35
35
( function: Function , context: FunctionPassContext ) in
36
36
37
- for block in function. blocks {
38
- // The last `release_value`` or `strong_release`` instruction before the
39
- // deallocation.
40
- var lastRelease : RefCountingInst ?
41
-
42
- for instruction in block. instructions {
43
- switch instruction {
44
- case let dealloc as DeallocStackRefInst :
45
- if let lastRel = lastRelease {
46
- // We only do the optimization for stack promoted object, because for
47
- // these we know that they don't have associated objects, which are
48
- // _not_ released by the deinit method.
49
- if !context. continueWithNextSubpassRun ( for: lastRel) {
50
- return
51
- }
52
- tryDevirtualizeRelease ( of: dealloc. allocRef, lastRelease: lastRel, context)
53
- lastRelease = nil
54
- }
55
- case let strongRelease as StrongReleaseInst :
56
- lastRelease = strongRelease
57
- case let releaseValue as ReleaseValueInst where releaseValue. value. type. containsSingleReference ( in: function) :
58
- lastRelease = releaseValue
59
- case is DeallocRefInst , is BeginDeallocRefInst :
60
- lastRelease = nil
61
- default :
62
- if instruction. mayRelease {
63
- lastRelease = nil
64
- }
37
+ for inst in function. instructions {
38
+ if let dealloc = inst as? DeallocStackRefInst {
39
+ if !context. continueWithNextSubpassRun ( for: dealloc) {
40
+ return
65
41
}
42
+ tryDevirtualizeRelease ( of: dealloc, context)
66
43
}
67
44
}
68
45
}
69
46
70
- /// Tries to de-virtualize the final release of a stack-promoted object.
71
- private func tryDevirtualizeRelease(
72
- of allocRef: AllocRefInstBase ,
73
- lastRelease: RefCountingInst ,
74
- _ context: FunctionPassContext
75
- ) {
76
- var downWalker = FindReleaseWalker ( release: lastRelease)
77
- guard let pathToRelease = downWalker. getPathToRelease ( from: allocRef) else {
47
+ private func tryDevirtualizeRelease( of dealloc: DeallocStackRefInst , _ context: FunctionPassContext ) {
48
+ guard let ( lastRelease, pathToRelease) = findLastRelease ( of: dealloc, context) else {
78
49
return
79
50
}
80
51
81
52
if !pathToRelease. isMaterializable {
82
53
return
83
54
}
84
55
56
+ let allocRef = dealloc. allocRef
85
57
var upWalker = FindAllocationWalker ( allocation: allocRef)
86
58
if upWalker. walkUp ( value: lastRelease. operand. value, path: pathToRelease) == . abortWalk {
87
59
return
@@ -120,23 +92,62 @@ private func tryDevirtualizeRelease(
120
92
context. erase ( instruction: lastRelease)
121
93
}
122
94
95
+ private func findLastRelease(
96
+ of dealloc: DeallocStackRefInst ,
97
+ _ context: FunctionPassContext
98
+ ) -> ( lastRelease: RefCountingInst , pathToRelease: SmallProjectionPath ) ? {
99
+ let allocRef = dealloc. allocRef
100
+
101
+ // Search for the final release in the same basic block of the dealloc.
102
+ for instruction in ReverseInstructionList ( first: dealloc. previous) {
103
+ switch instruction {
104
+ case let strongRelease as StrongReleaseInst :
105
+ if let pathToRelease = getPathToRelease ( from: allocRef, to: strongRelease) {
106
+ return ( strongRelease, pathToRelease)
107
+ }
108
+ case let releaseValue as ReleaseValueInst :
109
+ if releaseValue. value. type. containsSingleReference ( in: dealloc. parentFunction) {
110
+ if let pathToRelease = getPathToRelease ( from: allocRef, to: releaseValue) {
111
+ return ( releaseValue, pathToRelease)
112
+ }
113
+ }
114
+ case is BeginDeallocRefInst , is DeallocRefInst :
115
+ // Check if the last release was already de-virtualized.
116
+ if allocRef. escapes ( to: instruction, context) {
117
+ return nil
118
+ }
119
+ default :
120
+ break
121
+ }
122
+ if instruction. mayRelease && allocRef. escapes ( to: instruction, context) {
123
+ // This instruction may release the allocRef, which means that any release we find
124
+ // earlier in the block is not guaranteed to be the final release.
125
+ return nil
126
+ }
127
+ }
128
+ return nil
129
+ }
130
+
131
+ // If the release is a release_value it might release a struct which _contains_ the allocated object.
132
+ // Return a projection path to the contained object in this case.
133
+ private func getPathToRelease( from allocRef: AllocRefInstBase , to release: RefCountingInst ) -> SmallProjectionPath ? {
134
+ var downWalker = FindReleaseWalker ( release: release)
135
+ if downWalker. walkDownUses ( ofValue: allocRef, path: SmallProjectionPath ( ) ) == . continueWalk {
136
+ return downWalker. result
137
+ }
138
+ return nil
139
+ }
140
+
123
141
private struct FindReleaseWalker : ValueDefUseWalker {
124
142
private let release : RefCountingInst
125
- private var result : SmallProjectionPath ? = nil
143
+ private( set ) var result : SmallProjectionPath ? = nil
126
144
127
145
var walkDownCache = WalkerCache < SmallProjectionPath > ( )
128
146
129
147
init ( release: RefCountingInst ) {
130
148
self . release = release
131
149
}
132
150
133
- mutating func getPathToRelease( from allocRef: AllocRefInstBase ) -> SmallProjectionPath ? {
134
- if walkDownUses ( ofValue: allocRef, path: SmallProjectionPath ( ) ) == . continueWalk {
135
- return result
136
- }
137
- return nil
138
- }
139
-
140
151
mutating func leafUse( value: Operand , path: SmallProjectionPath ) -> WalkResult {
141
152
if value. instruction == release {
142
153
if let existingResult = result {
@@ -149,6 +160,23 @@ private struct FindReleaseWalker : ValueDefUseWalker {
149
160
}
150
161
}
151
162
163
+ private extension AllocRefInstBase {
164
+ func escapes( to instruction: Instruction , _ context: FunctionPassContext ) -> Bool {
165
+ return self . isEscaping ( using: EscapesToInstructionVisitor ( target: instruction) , context)
166
+ }
167
+ }
168
+
169
+ private struct EscapesToInstructionVisitor : EscapeVisitor {
170
+ let target : Instruction
171
+
172
+ mutating func visitUse( operand: Operand , path: EscapePath ) -> UseResult {
173
+ if operand. instruction == target {
174
+ return . abort
175
+ }
176
+ return . continueWalk
177
+ }
178
+ }
179
+
152
180
// Up-walker to find the root of a release instruction.
153
181
private struct FindAllocationWalker : ValueUseDefWalker {
154
182
private let allocInst : AllocRefInstBase
0 commit comments