@@ -152,13 +152,100 @@ private func addEnclosingValues(
152
152
return true
153
153
}
154
154
155
+ /// Replaces a phi with the unique incoming value if all incoming values are the same:
156
+ /// ```
157
+ /// bb1:
158
+ /// br bb3(%1)
159
+ /// bb2:
160
+ /// br bb3(%1)
161
+ /// bb3(%2 : $T): // Predecessors: bb1, bb2
162
+ /// use(%2)
163
+ /// ```
164
+ /// ->
165
+ /// ```
166
+ /// bb1:
167
+ /// br bb3
168
+ /// bb2:
169
+ /// br bb3
170
+ /// bb3:
171
+ /// use(%1)
172
+ /// ```
173
+ ///
174
+ func replacePhiWithIncomingValue( phi: Phi , _ context: some MutatingContext ) -> Bool {
175
+ if phi. predecessors. isEmpty {
176
+ return false
177
+ }
178
+ let uniqueIncomingValue = phi. incomingValues. first!
179
+ if !uniqueIncomingValue. parentFunction. hasOwnership {
180
+ // For the SSAUpdater it's only required to simplify phis in OSSA.
181
+ // This avoids that we need to handle cond_br instructions below.
182
+ return false
183
+ }
184
+ if phi. incomingValues. contains ( where: { $0 != uniqueIncomingValue } ) {
185
+ return false
186
+ }
187
+ if let borrowedFrom = phi. borrowedFrom {
188
+ borrowedFrom. uses. replaceAll ( with: uniqueIncomingValue, context)
189
+ context. erase ( instruction: borrowedFrom)
190
+ } else {
191
+ phi. value. uses. replaceAll ( with: uniqueIncomingValue, context)
192
+ }
193
+
194
+ let block = phi. value. parentBlock
195
+ for incomingOp in phi. incomingOperands {
196
+ let existingBranch = incomingOp. instruction as! BranchInst
197
+ let argsWithRemovedPhiOp = existingBranch. operands. filter { $0 != incomingOp } . map { $0. value }
198
+ Builder ( before: existingBranch, context) . createBranch ( to: block, arguments: argsWithRemovedPhiOp)
199
+ context. erase ( instruction: existingBranch)
200
+ }
201
+ block. eraseArgument ( at: phi. value. index, context)
202
+ return true
203
+ }
204
+
205
+ /// Replaces phis with the unique incoming values if all incoming values are the same.
206
+ /// This is needed after running the SSAUpdater for an existing OSSA value, because the updater can
207
+ /// insert unnecessary phis in the middle of the original liverange which breaks up the original
208
+ /// liverange into smaller ones:
209
+ /// ```
210
+ /// %1 = def_of_owned_value
211
+ /// %2 = begin_borrow %1
212
+ /// ...
213
+ /// br bb2(%1)
214
+ /// bb2(%3 : @owned $T): // inserted by SSAUpdater
215
+ /// ...
216
+ /// end_borrow %2 // use after end-of-lifetime!
217
+ /// destroy_value %3
218
+ /// ```
219
+ ///
220
+ /// It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange.
221
+ ///
222
+ func replacePhisWithIncomingValues( phis: [ Phi ] , _ context: some MutatingContext ) {
223
+ var currentPhis = phis
224
+ // Do this in a loop because replacing one phi might open up the opportunity for another phi
225
+ // and the order of phis in the array can be arbitrary.
226
+ while true {
227
+ var newPhis = [ Phi] ( )
228
+ for phi in currentPhis {
229
+ if !replacePhiWithIncomingValue( phi: phi, context) {
230
+ newPhis. append ( phi)
231
+ }
232
+ }
233
+ if newPhis. count == currentPhis. count {
234
+ return
235
+ }
236
+ currentPhis = newPhis
237
+ }
238
+ }
239
+
155
240
func registerGuaranteedPhiUpdater( ) {
156
241
BridgedUtilities . registerGuaranteedPhiUpdater (
242
+ // updateAllGuaranteedPhis
157
243
{ ( bridgedCtxt: BridgedPassContext , bridgedFunction: BridgedFunction ) in
158
244
let context = FunctionPassContext ( _bridged: bridgedCtxt)
159
245
let function = bridgedFunction. function;
160
246
updateGuaranteedPhis ( in: function, context)
161
247
} ,
248
+ // updateGuaranteedPhis
162
249
{ ( bridgedCtxt: BridgedPassContext , bridgedPhiArray: BridgedArrayRef ) in
163
250
let context = FunctionPassContext ( _bridged: bridgedCtxt)
164
251
var guaranteedPhis = Stack < Phi > ( context)
@@ -172,6 +259,15 @@ func registerGuaranteedPhiUpdater() {
172
259
}
173
260
}
174
261
updateGuaranteedPhis ( phis: guaranteedPhis, context)
262
+ } ,
263
+ // replacePhisWithIncomingValues
264
+ { ( bridgedCtxt: BridgedPassContext , bridgedPhiArray: BridgedArrayRef ) in
265
+ let context = FunctionPassContext ( _bridged: bridgedCtxt)
266
+ var phis = [ Phi] ( )
267
+ bridgedPhiArray. withElements ( ofType: BridgedValue . self) {
268
+ phis = $0. map { Phi ( $0. value) ! }
269
+ }
270
+ replacePhisWithIncomingValues ( phis: phis, context)
175
271
}
176
272
)
177
273
}
0 commit comments