@@ -201,7 +201,12 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue,
201201
202202// / Extend the lifetime of the convert_escape_to_noescape's operand to the end
203203// / 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+
205210// / NOTE: Since we are lifetime extending a copy that we have introduced, we do
206211// / not need to consider destroy_value emitted by SILGen unlike
207212// / copy_block_without_escaping which consumes its sentinel parameter. Unlike
@@ -215,91 +220,108 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
215220 auto optionalEscapingClosureTy = SILType::getOptionalType (escapingClosureTy);
216221 auto loc = RegularLocation::getAutoGeneratedLocation ();
217222
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
221247 // the exit blocks of the function.
222248 //
223249 // (*) In fact we can't use the SILSSAUpdater::GetValueInMiddleOfBlock.
224250 if (cvt->getParent () == cvt->getFunction ()->getEntryBlock ()) {
225- auto *innerCVI =
226- SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
251+ auto *copy = SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
227252 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; });
236255 return ;
237256 }
238257
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.
263259 // 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
265261 // 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);
288281 }
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 );
294287 }
295288
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);
303325}
304326
305327static SILInstruction *lookThroughRebastractionUsers (
@@ -979,8 +1001,8 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
9791001
9801002 for (auto &block : fn) {
9811003 SILSSAUpdater updater;
982- for (SILInstruction *inst : updater.getDeleter ().updatingRange (&block)) {
9831004
1005+ for (SILInstruction *inst : updater.getDeleter ().updatingRange (&block)) {
9841006 // Handle, copy_block_without_escaping instructions.
9851007 if (auto *cb = dyn_cast<CopyBlockWithoutEscapingInst>(inst)) {
9861008 if (fixupCopyBlockWithoutEscaping (cb, updater.getDeleter (), modifiedCFG)) {
0 commit comments