@@ -201,7 +201,12 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue,
201
201
202
202
// / Extend the lifetime of the convert_escape_to_noescape's operand to the end
203
203
// / of the function.
204
- // /
204
+ // / Create a copy of the escaping closure operand and end its lifetime at
205
+ // / function exits. Since, the cvt may not be dominating function exits, we
206
+ // / need to create an optional and use the SSAUpdater to extend the lifetime. In
207
+ // / order to prevent the optional being optimized away, create a borrow scope
208
+ // / and insert a mark_dependence of the non escaping closure on the borrow.
209
+
205
210
// / NOTE: Since we are lifetime extending a copy that we have introduced, we do
206
211
// / not need to consider destroy_value emitted by SILGen unlike
207
212
// / copy_block_without_escaping which consumes its sentinel parameter. Unlike
@@ -215,91 +220,108 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
215
220
auto optionalEscapingClosureTy = SILType::getOptionalType (escapingClosureTy);
216
221
auto loc = RegularLocation::getAutoGeneratedLocation ();
217
222
218
- // If our Cvt is in the initial block, we do not need to use the SSA updater
219
- // since we know Cvt can not be in a loop and must dominate all exits
220
- // (*). Just insert a copy of the escaping closure at the Cvt and destroys at
223
+ SmallVector<SILBasicBlock *, 4 > exitingBlocks;
224
+ fn.findExitingBlocks (exitingBlocks);
225
+
226
+ auto createLifetimeEnd = [](SILLocation loc, SILInstruction *insertPt,
227
+ SILValue value) {
228
+ SILBuilderWithScope builder (insertPt);
229
+ if (value.getOwnershipKind () == OwnershipKind::Owned) {
230
+ builder.emitDestroyOperation (loc, value);
231
+ return ;
232
+ }
233
+ builder.emitEndBorrowOperation (loc, value);
234
+ };
235
+ auto createLifetimeEndAtFunctionExits =
236
+ [&](std::function<SILValue (SILBasicBlock *)> getValue) {
237
+ for (auto *block : exitingBlocks) {
238
+ auto *safeDestructionPoint =
239
+ getDeinitSafeClosureDestructionPoint (block);
240
+ createLifetimeEnd (loc, safeDestructionPoint, getValue (block));
241
+ }
242
+ };
243
+
244
+ // If our cvt is in the initial block, we do not need to use the SSA updater
245
+ // since we know cvt cannot be in a loop and must dominate all exits
246
+ // (*). Just insert a copy of the escaping closure at the cvt and destroys at
221
247
// the exit blocks of the function.
222
248
//
223
249
// (*) In fact we can't use the SILSSAUpdater::GetValueInMiddleOfBlock.
224
250
if (cvt->getParent () == cvt->getFunction ()->getEntryBlock ()) {
225
- auto *innerCVI =
226
- SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
251
+ auto *copy = SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
227
252
cvt->setLifetimeGuaranteed ();
228
- cvt->setOperand (innerCVI);
229
- SmallVector<SILBasicBlock *, 4 > exitingBlocks;
230
- fn.findExitingBlocks (exitingBlocks);
231
-
232
- for (auto *block : exitingBlocks) {
233
- auto *safeDestructPoint = getDeinitSafeClosureDestructionPoint (block);
234
- SILBuilderWithScope (safeDestructPoint).createDestroyValue (loc, innerCVI);
235
- }
253
+ cvt->setOperand (copy);
254
+ createLifetimeEndAtFunctionExits ([©](SILBasicBlock *) { return copy; });
236
255
return ;
237
256
}
238
257
239
- // Ok. At this point we know that Cvt is not in the entry block... so we can
240
- // use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's
241
- // lifetime respecting loops.
242
- SmallVector<SILPhiArgument *, 8 > insertedPhis;
243
- updater.setInsertedPhis (&insertedPhis);
244
- updater.initialize (optionalEscapingClosureTy, fn.hasOwnership ()
245
- ? OwnershipKind::Owned
246
- : OwnershipKind::None);
247
-
248
- // Create an Optional<() -> ()>.none in the entry block of the function and
249
- // add it as an available value to the SSAUpdater.
250
- //
251
- // Since we know that Cvt is not in the entry block and this must be, we know
252
- // that it is safe to use the SSAUpdater's getValueInMiddleOfBlock with this
253
- // value.
254
- SILValue entryBlockOptionalNone = [&]() -> SILValue {
255
- SILBuilderWithScope b (fn.getEntryBlock ()->begin ());
256
- return b.createOptionalNone (loc, optionalEscapingClosureTy);
257
- }();
258
- updater.addAvailableValue (fn.getEntryBlock (), entryBlockOptionalNone);
259
-
260
- // Create a copy of the convert_escape_to_no_escape and add it as an available
261
- // value to the SSA updater.
262
- //
258
+ // Create a copy of the convert_escape_to_no_escape.
263
259
// NOTE: The SSAUpdater does not support providing multiple values in the same
264
- // block without extra work. So the fact that Cvt is not in the entry block
260
+ // block without extra work. So the fact that cvt is not in the entry block
265
261
// means that we don't have to worry about overwriting the .none value.
266
- auto *cvi = [&]() -> CopyValueInst * {
267
- auto *innerCVI =
268
- SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
269
- cvt->setLifetimeGuaranteed ();
270
- cvt->setOperand (innerCVI);
271
- SILBuilderWithScope b (std::next (cvt->getIterator ()));
272
- updater.addAvailableValue (
273
- cvt->getParent (),
274
- b.createOptionalSome (loc, innerCVI, optionalEscapingClosureTy));
275
- return innerCVI;
276
- }();
277
-
278
- // Then we use the SSA updater to insert a destroy_value before the cvt and at
279
- // the reachable exit blocks.
280
- SmallVector<SILBasicBlock *, 4 > exitingBlocks;
281
- findReachableExitBlocks (cvt, exitingBlocks);
282
-
283
- {
284
- // Before the copy value, insert an extra destroy_value to handle
285
- // loops. Since we used our enum value this is safe.
286
- SILValue v = updater.getValueInMiddleOfBlock (cvi->getParent ());
287
- SILBuilderWithScope (cvi).createDestroyValue (loc, v);
262
+ auto *copy = SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
263
+ cvt->setLifetimeGuaranteed ();
264
+ cvt->setOperand (copy);
265
+
266
+ // Create an optional some to extend the lifetime of copy until function
267
+ // exits.
268
+ SILBuilderWithScope lifetimeExtendBuilder (std::next (cvt->getIterator ()));
269
+ auto *optionalSome = lifetimeExtendBuilder.createOptionalSome (
270
+ loc, copy, optionalEscapingClosureTy);
271
+
272
+ // Create a borrow scope and a mark_dependence to prevent the enum being
273
+ // optimized away.
274
+ auto *borrow = lifetimeExtendBuilder.createBeginBorrow (loc, optionalSome);
275
+ auto *mdi = lifetimeExtendBuilder.createMarkDependence (loc, cvt, borrow);
276
+
277
+ // Replace all uses of the non escaping closure with mark_dependence
278
+ SmallVector<Operand *, 4 > convertUses;
279
+ for (auto *cvtUse : cvt->getUses ()) {
280
+ convertUses.push_back (cvtUse);
288
281
}
289
-
290
- for ( auto *block : exitingBlocks) {
291
- auto *safeDestructionPt = getDeinitSafeClosureDestructionPoint (block);
292
- SILValue v = updater. getValueAtEndOfBlock (block) ;
293
- SILBuilderWithScope (safeDestructionPt). createDestroyValue (loc, v );
282
+ for ( auto *cvtUse : convertUses) {
283
+ auto *cvtUser = cvtUse-> getUser ();
284
+ if (cvtUser == mdi)
285
+ continue ;
286
+ cvtUser-> setOperand (cvtUse-> getOperandNumber (), mdi );
294
287
}
295
288
296
- // Finally, we need to prune phis inserted by the SSA updater that only take
297
- // the .none from the entry block.
298
- //
299
- // TODO: Should we sort inserted phis before or after we initialize
300
- // the worklist or maybe backwards? We should investigate how the
301
- // SSA updater adds phi nodes to this list to resolve this question.
302
- cleanupDeadTrivialPhiArgs (entryBlockOptionalNone, insertedPhis);
289
+ auto fixupSILForLifetimeExtension = [&](SILValue value) {
290
+ // Use SSAUpdater to find insertion points for lifetime ends.
291
+ updater.initialize (optionalEscapingClosureTy, value.getOwnershipKind ());
292
+ SmallVector<SILPhiArgument *, 8 > insertedPhis;
293
+ updater.setInsertedPhis (&insertedPhis);
294
+
295
+ // Create an optional none at the function entry.
296
+ auto *optionalNone =
297
+ SILBuilderWithScope (fn.getEntryBlock ()->begin ())
298
+ .createOptionalNone (loc, optionalEscapingClosureTy);
299
+ updater.addAvailableValue (fn.getEntryBlock (), optionalNone);
300
+ updater.addAvailableValue (value->getParentBlock (), value);
301
+ {
302
+ // Since value maybe in a loop, insert an extra lifetime end. Since we
303
+ // used our enum value, this is safe.
304
+ SILValue midValue =
305
+ updater.getValueInMiddleOfBlock (value->getParentBlock ());
306
+ createLifetimeEnd (loc, cvt, midValue);
307
+ }
308
+
309
+ // Insert lifetime ends.
310
+ createLifetimeEndAtFunctionExits ([&updater](SILBasicBlock *block) {
311
+ return updater.getValueAtEndOfBlock (block);
312
+ });
313
+
314
+ // Prune the phis inserted by the SSA updater that only take
315
+ // the .none from the entry block.
316
+ // TODO: Should we sort inserted phis before or after we initialize
317
+ // the worklist or maybe backwards? We should investigate how the
318
+ // SSA updater adds phi nodes to this list to resolve this question.
319
+ cleanupDeadTrivialPhiArgs (optionalNone, insertedPhis);
320
+ };
321
+
322
+ // Use the SSAUpdater to create lifetime ends for the copy and the borrow.
323
+ fixupSILForLifetimeExtension (borrow);
324
+ fixupSILForLifetimeExtension (optionalSome);
303
325
}
304
326
305
327
static SILInstruction *lookThroughRebastractionUsers (
@@ -979,8 +1001,8 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
979
1001
980
1002
for (auto &block : fn) {
981
1003
SILSSAUpdater updater;
982
- for (SILInstruction *inst : updater.getDeleter ().updatingRange (&block)) {
983
1004
1005
+ for (SILInstruction *inst : updater.getDeleter ().updatingRange (&block)) {
984
1006
// Handle, copy_block_without_escaping instructions.
985
1007
if (auto *cb = dyn_cast<CopyBlockWithoutEscapingInst>(inst)) {
986
1008
if (fixupCopyBlockWithoutEscaping (cb, updater.getDeleter (), modifiedCFG)) {
0 commit comments