@@ -122,7 +122,11 @@ private struct Uses {
122
122
// Exit blocks of the load/copy_value's liverange which don't have a destroy.
123
123
// Those are successor blocks of terminators, like `switch_enum`, which do _not_ forward the value.
124
124
// E.g. the none-case of a switch_enum of an Optional.
125
- private( set) var nonDestroyingLiverangeExits : Stack < BasicBlock >
125
+ private( set) var nonDestroyingLiverangeExits : Stack < Instruction >
126
+
127
+ var allLifetimeEndingInstructions : [ Instruction ] {
128
+ Array ( destroys. lazy. map { $0 } ) + Array( nonDestroyingLiverangeExits)
129
+ }
126
130
127
131
private( set) var usersInDeadEndBlocks : Stack < Instruction >
128
132
@@ -179,8 +183,13 @@ private struct Uses {
179
183
// A terminator instruction can implicitly end the lifetime of its operand in a success block,
180
184
// e.g. a `switch_enum` with a non-payload case block. Such success blocks need an `end_borrow`, though.
181
185
for succ in termInst. successors where !succ. arguments. contains ( where: { $0. ownership == . owned} ) {
182
- nonDestroyingLiverangeExits. append ( succ)
186
+ nonDestroyingLiverangeExits. append ( succ. instructions . first! )
183
187
}
188
+ } else if !forwardingInst. forwardedResults. contains ( where: { $0. ownership == . owned } ) {
189
+ // The forwarding instruction has no owned result, which means it ends the lifetime of its owned operand.
190
+ // This can happen with an `unchecked_enum_data` which extracts a trivial payload out of a
191
+ // non-trivial enum.
192
+ nonDestroyingLiverangeExits. append ( forwardingInst. next!)
184
193
}
185
194
}
186
195
@@ -268,7 +277,7 @@ private func remove(copy: CopyValueInst, collectedUses: Uses, liverange: Instruc
268
277
context. erase ( instructions: collectedUses. destroys)
269
278
}
270
279
271
- // Handle the special case if the `load` or `copy_valuw ` is immediately followed by a single `move_value`.
280
+ // Handle the special case if the `load` or `copy_value ` is immediately followed by a single `move_value`.
272
281
// In this case we have to preserve the move's flags by inserting a `begin_borrow` with the same flags.
273
282
// For example:
274
283
//
@@ -314,15 +323,11 @@ private func createEndBorrows(for beginBorrow: Value, atEndOf liverange: Instruc
314
323
// destroy_value %2
315
324
// destroy_value %3 // The final destroy. Here we need to create the `end_borrow`(s)
316
325
//
317
- for destroy in collectedUses. destroys where !liverange. contains ( destroy) {
318
- let builder = Builder ( before: destroy, context)
319
- builder. createEndBorrow ( of: beginBorrow)
320
- }
321
- for liverangeExitBlock in collectedUses. nonDestroyingLiverangeExits where
322
- !liverange. blockRange. contains ( liverangeExitBlock)
323
- {
324
- let builder = Builder ( atBeginOf: liverangeExitBlock, context)
325
- builder. createEndBorrow ( of: beginBorrow)
326
+ for endInst in collectedUses. allLifetimeEndingInstructions {
327
+ if !liverange. contains ( endInst) {
328
+ let builder = Builder ( before: endInst, context)
329
+ builder. createEndBorrow ( of: beginBorrow)
330
+ }
326
331
}
327
332
}
328
333
0 commit comments