14
14
#include " SILGenFunctionBuilder.h"
15
15
#include " RValue.h"
16
16
#include " ArgumentScope.h"
17
+ #include " llvm/ADT/SmallSet.h"
17
18
#include " swift/AST/GenericSignature.h"
18
19
#include " swift/AST/SubstitutionMap.h"
19
20
#include " swift/SIL/TypeLowering.h"
@@ -219,6 +220,140 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc,
219
220
}
220
221
}
221
222
223
+ llvm::SmallSetVector<VarDecl*, 4 > findRecursiveLinks (DeclContext* DC, ClassDecl *cd) {
224
+ auto SelfTy = DC->mapTypeIntoContext (cd->getDeclaredType ());
225
+
226
+ // Collect all stored properties that would form a recursive structure,
227
+ // so we can remove the recursion and prevent the call stack from
228
+ // overflowing.
229
+ llvm::SmallSetVector<VarDecl*, 4 > recursiveLinks;
230
+ for (VarDecl *vd : cd->getStoredProperties ()) {
231
+ auto Ty = vd->getType ()->getOptionalObjectType ();
232
+ if (Ty && Ty->getCanonicalType () == SelfTy->getCanonicalType ()) {
233
+ recursiveLinks.insert (vd);
234
+ }
235
+ }
236
+
237
+ // NOTE: Right now we only optimize linear recursion, so if there is more than one link,
238
+ // clear out the set and don't perform any recursion optimization.
239
+ if (recursiveLinks.size () < 1 ) {
240
+ recursiveLinks.clear ();
241
+ }
242
+
243
+ return recursiveLinks;
244
+ }
245
+
246
+
247
+ void SILGenFunction::emitRecursiveChainDestruction (ManagedValue selfValue,
248
+ ClassDecl *cd,
249
+ SmallSetVector<VarDecl*, 4 > recursiveLinks,
250
+ CleanupLocation cleanupLoc) {
251
+ auto SelfTy = F.mapTypeIntoContext (cd->getDeclaredType ());
252
+
253
+ assert (recursiveLinks.size () <= 1 && " Only linear recursion supported." );
254
+
255
+ auto SelfTyLowered = getTypeLowering (SelfTy).getLoweredType ();
256
+ for (VarDecl* vd : recursiveLinks) {
257
+ SILBasicBlock* cleanBB = createBasicBlock ();
258
+ SILBasicBlock* noneBB = createBasicBlock ();
259
+ SILBasicBlock* notUniqueBB = createBasicBlock ();
260
+ SILBasicBlock* uniqueBB = createBasicBlock ();
261
+ SILBasicBlock* someBB = createBasicBlock ();
262
+ SILBasicBlock* loopBB = createBasicBlock ();
263
+
264
+ // var iter = self.link
265
+ // self.link = nil
266
+ auto Ty = getTypeLowering (vd->getType ()).getLoweredType ();
267
+ auto optionalNone = B.createOptionalNone (cleanupLoc, Ty);
268
+ SILValue varAddr =
269
+ B.createRefElementAddr (cleanupLoc, selfValue.getValue (), vd,
270
+ Ty.getAddressType ());
271
+ auto iterAddr = B.createAllocStack (cleanupLoc, Ty);
272
+ SILValue addr = B.createBeginAccess (
273
+ cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static,
274
+ false /* noNestedConflict*/ , false /* fromBuiltin*/ );
275
+ SILValue iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
276
+ B.createStore (cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Assign);
277
+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
278
+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init);
279
+
280
+ B.createBranch (cleanupLoc, loopBB);
281
+
282
+ // while iter != nil {
283
+ B.emitBlock (loopBB);
284
+ SILValue operand = B.createLoad (cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy);
285
+ auto operandCopy = B.createCopyValue (cleanupLoc, operand);
286
+ auto operandAddr = B.createAllocStack (cleanupLoc, Ty);
287
+ B.createStore (cleanupLoc, operandCopy, operandAddr, StoreOwnershipQualifier::Init);
288
+ B.createDestroyValue (cleanupLoc, operand);
289
+ B.createSwitchEnumAddr (
290
+ cleanupLoc, operandAddr, nullptr ,
291
+ {{getASTContext ().getOptionalSomeDecl (), someBB},
292
+ {std::make_pair (getASTContext ().getOptionalNoneDecl (), noneBB)}});
293
+
294
+ // if isKnownUniquelyReferenced(&iter) {
295
+ B.emitBlock (someBB);
296
+ B.createDestroyAddr (cleanupLoc, operandAddr);
297
+ B.createDeallocStack (cleanupLoc, operandAddr);
298
+ auto isUnique = B.createIsUnique (cleanupLoc, iterAddr);
299
+ B.createCondBranch (cleanupLoc, isUnique, uniqueBB, notUniqueBB);
300
+
301
+
302
+ // we have a uniquely referenced link, so we need to deinit
303
+ B.emitBlock (uniqueBB);
304
+
305
+ // NOTE: We increment the ref count of the tail instead of unlinking it,
306
+ // because custom deinit implementations of subclasses may access
307
+ // it and it would be semantically wrong to unset it before that.
308
+ // Making the tail non-uniquely referenced prevents the recursion.
309
+
310
+ // let tail = iter.unsafelyUnwrapped.next
311
+ // iter = tail
312
+ SILValue _iter = B.createLoad (cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy);
313
+ auto iterBorrow = B.createBeginBorrow (cleanupLoc, _iter);
314
+ auto iterBorrowAddr = B.createAllocStack (cleanupLoc, Ty);
315
+ B.createStoreBorrow (cleanupLoc, iterBorrow, iterBorrowAddr);
316
+ auto xx = B.createLoadBorrow (cleanupLoc, iterBorrowAddr);
317
+ auto * link = B.createUncheckedEnumData (cleanupLoc,
318
+ xx,
319
+ getASTContext ().getOptionalSomeDecl (),
320
+ SelfTyLowered);
321
+ varAddr = B.createRefElementAddr (cleanupLoc,
322
+ link,
323
+ vd,
324
+ Ty.getAddressType ());
325
+
326
+ addr = B.createBeginAccess (
327
+ cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
328
+ false /* noNestedConflict */ , false /* fromBuiltin*/ );
329
+ iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
330
+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
331
+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
332
+
333
+ B.createEndBorrow (cleanupLoc, xx);
334
+ B.createEndBorrow (cleanupLoc, iterBorrow);
335
+
336
+ B.createDestroyValue (cleanupLoc, _iter);
337
+ B.createDeallocStack (cleanupLoc, iterBorrowAddr);
338
+
339
+ B.createBranch (cleanupLoc, loopBB);
340
+
341
+ // the next link in the chain is not unique, so we are done here
342
+ B.emitBlock (notUniqueBB);
343
+ B.createBranch (cleanupLoc, cleanBB);
344
+
345
+ // we reached the end of the chain
346
+ B.emitBlock (noneBB);
347
+ B.createDeallocStack (cleanupLoc, operandAddr);
348
+ B.createBranch (cleanupLoc, cleanBB);
349
+
350
+
351
+ B.emitBlock (cleanBB);
352
+ B.createDestroyAddr (cleanupLoc, iterAddr);
353
+ B.createDeallocStack (cleanupLoc, iterAddr);
354
+ }
355
+ }
356
+
222
357
void SILGenFunction::emitClassMemberDestruction (ManagedValue selfValue,
223
358
ClassDecl *cd,
224
359
CleanupLocation cleanupLoc) {
@@ -239,21 +374,28 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
239
374
// / A distributed actor may be 'remote' in which case there is no need to
240
375
// / destroy "all" members, because they never had storage to begin with.
241
376
if (cd->isDistributedActor ()) {
242
- finishBB = createBasicBlock ();
243
377
normalMemberDestroyBB = createBasicBlock ();
244
-
378
+ finishBB = createBasicBlock ();
245
379
emitDistributedActorClassMemberDestruction (cleanupLoc, selfValue, cd,
246
380
normalMemberDestroyBB,
247
381
finishBB);
248
382
}
249
383
384
+ auto recursiveLinks = findRecursiveLinks (F.getDeclContext (), cd);
385
+
250
386
// / Destroy all members.
251
387
{
252
388
if (normalMemberDestroyBB)
253
389
B.emitBlock (normalMemberDestroyBB);
254
390
255
- for (VarDecl *vd : cd->getStoredProperties ())
391
+ for (VarDecl *vd : cd->getStoredProperties ()) {
392
+ if (recursiveLinks.contains (vd))
393
+ continue ;
256
394
destroyClassMember (cleanupLoc, selfValue, vd);
395
+ }
396
+
397
+ if (!recursiveLinks.empty ())
398
+ emitRecursiveChainDestruction (selfValue, cd, recursiveLinks, cleanupLoc);
257
399
258
400
if (finishBB)
259
401
B.createBranch (cleanupLoc, finishBB);
0 commit comments