10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
13
+ #include " ArgumentScope.h"
14
+ #include " RValue.h"
13
15
#include " SILGenFunction.h"
14
16
#include " SILGenFunctionBuilder.h"
15
- #include " RValue.h"
16
- #include " ArgumentScope.h"
17
+ #include " SwitchEnumBuilder.h"
17
18
#include " swift/AST/GenericSignature.h"
18
19
#include " swift/AST/SubstitutionMap.h"
19
20
#include " swift/SIL/TypeLowering.h"
21
+ #include " llvm/ADT/SmallSet.h"
20
22
21
23
using namespace swift ;
22
24
using namespace Lowering ;
@@ -219,6 +221,142 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc,
219
221
}
220
222
}
221
223
224
+ // / Finds stored properties that have the same type as `cd` and thus form
225
+ // / a recursive structure.
226
+ // /
227
+ // / Example:
228
+ // /
229
+ // / class Node<T> {
230
+ // / let element: T
231
+ // / let next: Node<T>?
232
+ // / }
233
+ // /
234
+ // / In the above example `next` is a recursive link and would be recognized
235
+ // / by this function and added to the result set.
236
+ static void findRecursiveLinks (ClassDecl *cd,
237
+ llvm::SmallSetVector<VarDecl *, 4 > &result) {
238
+ auto selfTy = cd->getDeclaredInterfaceType ();
239
+
240
+ // Collect all stored properties that would form a recursive structure,
241
+ // so we can remove the recursion and prevent the call stack from
242
+ // overflowing.
243
+ for (VarDecl *vd : cd->getStoredProperties ()) {
244
+ auto Ty = vd->getInterfaceType ()->getOptionalObjectType ();
245
+ if (Ty && Ty->getCanonicalType () == selfTy->getCanonicalType ()) {
246
+ result.insert (vd);
247
+ }
248
+ }
249
+
250
+ // NOTE: Right now we only optimize linear recursion, so if there is more
251
+ // than one stored property of the same type, clear out the set and don't
252
+ // perform any recursion optimization.
253
+ if (result.size () > 1 ) {
254
+ result.clear ();
255
+ }
256
+ }
257
+
258
+ void SILGenFunction::emitRecursiveChainDestruction (ManagedValue selfValue,
259
+ ClassDecl *cd,
260
+ VarDecl *recursiveLink,
261
+ CleanupLocation cleanupLoc) {
262
+ auto selfTy = F.mapTypeIntoContext (cd->getDeclaredInterfaceType ());
263
+
264
+ auto selfTyLowered = getTypeLowering (selfTy).getLoweredType ();
265
+
266
+ SILBasicBlock *cleanBB = createBasicBlock ();
267
+ SILBasicBlock *noneBB = createBasicBlock ();
268
+ SILBasicBlock *notUniqueBB = createBasicBlock ();
269
+ SILBasicBlock *uniqueBB = createBasicBlock ();
270
+ SILBasicBlock *someBB = createBasicBlock ();
271
+ SILBasicBlock *loopBB = createBasicBlock ();
272
+
273
+ // var iter = self.link
274
+ // self.link = nil
275
+ auto Ty = getTypeLowering (F.mapTypeIntoContext (recursiveLink->getInterfaceType ())).getLoweredType ();
276
+ auto optionalNone = B.createOptionalNone (cleanupLoc, Ty);
277
+ SILValue varAddr =
278
+ B.createRefElementAddr (cleanupLoc, selfValue.getValue (), recursiveLink,
279
+ Ty.getAddressType ());
280
+ auto *iterAddr = B.createAllocStack (cleanupLoc, Ty);
281
+ SILValue addr = B.createBeginAccess (
282
+ cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static,
283
+ true /* noNestedConflict*/ , false /* fromBuiltin*/ );
284
+ SILValue iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Take);
285
+ B.createStore (cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Init);
286
+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
287
+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init);
288
+
289
+ B.createBranch (cleanupLoc, loopBB);
290
+
291
+ // while iter != nil {
292
+ {
293
+ B.emitBlock (loopBB);
294
+ auto iterBorrow =
295
+ ManagedValue::forUnmanaged (iterAddr).borrow (*this , cleanupLoc);
296
+ SwitchEnumBuilder switchBuilder (B, cleanupLoc, iterBorrow);
297
+ switchBuilder.addOptionalSomeCase (someBB);
298
+ switchBuilder.addOptionalNoneCase (noneBB);
299
+ std::move (switchBuilder).emit ();
300
+ }
301
+
302
+ // if isKnownUniquelyReferenced(&iter) {
303
+ {
304
+ B.emitBlock (someBB);
305
+ auto isUnique = B.createIsUnique (cleanupLoc, iterAddr);
306
+ B.createCondBranch (cleanupLoc, isUnique, uniqueBB, notUniqueBB);
307
+ }
308
+
309
+ // we have a uniquely referenced link, so we need to deinit
310
+ {
311
+ B.emitBlock (uniqueBB);
312
+
313
+ // let tail = iter.unsafelyUnwrapped.next
314
+ // iter = tail
315
+ SILValue iterBorrow = B.createLoadBorrow (cleanupLoc, iterAddr);
316
+ auto *link = B.createUncheckedEnumData (
317
+ cleanupLoc, iterBorrow, getASTContext ().getOptionalSomeDecl (),
318
+ selfTyLowered);
319
+
320
+ varAddr = B.createRefElementAddr (cleanupLoc, link, recursiveLink,
321
+ Ty.getAddressType ());
322
+
323
+ addr = B.createBeginAccess (
324
+ cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
325
+ true /* noNestedConflict */ , false /* fromBuiltin*/ );
326
+
327
+ // The deinit of `iter` will decrement the ref count of the field
328
+ // containing the next element and potentially leading to its
329
+ // deinitialization, causing the recursion. The prevent that,
330
+ // we `load [copy]` here to ensure the object stays alive until
331
+ // we explicitly release it in the next step of the iteration.
332
+ iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
333
+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
334
+ B.createEndBorrow (cleanupLoc, iterBorrow);
335
+
336
+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
337
+
338
+ B.createBranch (cleanupLoc, loopBB);
339
+ }
340
+
341
+ // the next link in the chain is not unique, so we are done here
342
+ {
343
+ B.emitBlock (notUniqueBB);
344
+ B.createBranch (cleanupLoc, cleanBB);
345
+ }
346
+
347
+ // we reached the end of the chain
348
+ {
349
+ B.emitBlock (noneBB);
350
+ B.createBranch (cleanupLoc, cleanBB);
351
+ }
352
+
353
+ {
354
+ B.emitBlock (cleanBB);
355
+ B.createDestroyAddr (cleanupLoc, iterAddr);
356
+ B.createDeallocStack (cleanupLoc, iterAddr);
357
+ }
358
+ }
359
+
222
360
void SILGenFunction::emitClassMemberDestruction (ManagedValue selfValue,
223
361
ClassDecl *cd,
224
362
CleanupLocation cleanupLoc) {
@@ -231,10 +369,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
231
369
// / For other cases, the basic blocks are not necessary and the destructor
232
370
// / can just emit all the normal destruction code right into the current block.
233
371
// If set, used as the basic block for the destroying of all members.
234
- SILBasicBlock* normalMemberDestroyBB = nullptr ;
372
+ SILBasicBlock * normalMemberDestroyBB = nullptr ;
235
373
// If set, used as the basic block after members have been destroyed,
236
374
// and we're ready to perform final cleanups before returning.
237
- SILBasicBlock* finishBB = nullptr ;
375
+ SILBasicBlock * finishBB = nullptr ;
238
376
239
377
// / A distributed actor may be 'remote' in which case there is no need to
240
378
// / destroy "all" members, because they never had storage to begin with.
@@ -247,13 +385,29 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
247
385
finishBB);
248
386
}
249
387
388
+ // Before we destroy all fields, we check if any of them are
389
+ // recursively the same type as `self`, so we can iteratively
390
+ // deinitialize them, to prevent deep recursion and potential
391
+ // stack overflows.
392
+
393
+ llvm::SmallSetVector<VarDecl *, 4 > recursiveLinks;
394
+ findRecursiveLinks (cd, recursiveLinks);
395
+
250
396
// / Destroy all members.
251
397
{
252
398
if (normalMemberDestroyBB)
253
399
B.emitBlock (normalMemberDestroyBB);
254
400
255
- for (VarDecl *vd : cd->getStoredProperties ())
401
+ for (VarDecl *vd : cd->getStoredProperties ()) {
402
+ if (recursiveLinks.contains (vd))
403
+ continue ;
256
404
destroyClassMember (cleanupLoc, selfValue, vd);
405
+ }
406
+
407
+ if (!recursiveLinks.empty ()) {
408
+ assert (recursiveLinks.size () == 1 && " Only linear recursion supported." );
409
+ emitRecursiveChainDestruction (selfValue, cd, recursiveLinks[0 ], cleanupLoc);
410
+ }
257
411
258
412
if (finishBB)
259
413
B.createBranch (cleanupLoc, finishBB);
0 commit comments