29
29
#include " swift/SIL/BasicBlockUtils.h"
30
30
#include " swift/SIL/DebugUtils.h"
31
31
#include " swift/SIL/InstructionUtils.h"
32
+ #include " swift/SIL/OwnershipUtils.h"
32
33
#include " swift/SIL/Projection.h"
33
34
#include " swift/SIL/SILArgument.h"
34
35
#include " swift/SIL/SILDeclRef.h"
@@ -127,6 +128,10 @@ static bool isDestroyArray(SILInstruction *inst) {
127
128
// / Analyze the destructor for the class of ARI to see if any instructions in it
128
129
// / could have side effects on the program outside the destructor. If it does
129
130
// / not, then we can eliminate the destructor.
131
+ // / TODO: Most default destructors with non-trivial elements will have a
132
+ // / destroy_addr of the non-trivial element in the destructor, this analysis
133
+ // / will return as having side-effects in such cases, leading to conservative
134
+ // / results. Check if we can do better here.
130
135
static DestructorEffects doesDestructorHaveSideEffects (AllocRefInstBase *ARI) {
131
136
SILFunction *Fn = getDestructor (ARI);
132
137
// If we can't find a constructor then assume it has side effects.
@@ -173,13 +178,22 @@ static DestructorEffects doesDestructorHaveSideEffects(AllocRefInstBase *ARI) {
173
178
assert (RefInst->getNumOperands () == 1 &&
174
179
" Make sure RefInst only has one argument." );
175
180
LLVM_DEBUG (llvm::dbgs () << " SAFE! Ref count operation on "
176
- " Self.\n " );
181
+ " Self.\n " );
182
+ continue ;
183
+ }
184
+ LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
185
+ " not on self.\n " );
186
+ return DestructorEffects::Unknown;
187
+ }
188
+ if (auto *destroy = dyn_cast<DestroyValueInst>(&I)) {
189
+ if (stripCasts (destroy->getOperand ()) == Self) {
190
+ LLVM_DEBUG (llvm::dbgs () << " SAFE! Ref count operation on "
191
+ " Self.\n " );
177
192
continue ;
178
- } else {
179
- LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
180
- " not on self.\n " );
181
- return DestructorEffects::Unknown;
182
193
}
194
+ LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
195
+ " not on self.\n " );
196
+ return DestructorEffects::Unknown;
183
197
}
184
198
185
199
// dealloc_stack can be ignored.
@@ -242,7 +256,15 @@ static DestructorEffects doesDestructorHaveSideEffects(AllocRefInstBase *ARI) {
242
256
// / alloc_ref alive.
243
257
static bool canZapInstruction (SILInstruction *Inst, bool acceptRefCountInsts,
244
258
bool onlyAcceptTrivialStores) {
245
- if (isa<SetDeallocatingInst>(Inst) || isa<FixLifetimeInst>(Inst))
259
+ if (isa<DestroyValueInst>(Inst)) {
260
+ return acceptRefCountInsts;
261
+ }
262
+ if (isa<CopyValueInst>(Inst) || isa<BeginBorrowInst>(Inst) ||
263
+ isa<MoveValueInst>(Inst)) {
264
+ return true ;
265
+ }
266
+ if (isa<SetDeallocatingInst>(Inst) || isa<FixLifetimeInst>(Inst) ||
267
+ isa<EndBorrowInst>(Inst))
246
268
return true ;
247
269
248
270
// It is ok to eliminate various retains/releases. We are either removing
@@ -267,13 +289,17 @@ static bool canZapInstruction(SILInstruction *Inst, bool acceptRefCountInsts,
267
289
if (isa<DestroyAddrInst>(Inst))
268
290
return true ;
269
291
270
- // If we see a store here, we have already checked that we are storing into
271
- // the pointer before we added it to the worklist, so we can skip it.
292
+ // We have already checked that we are storing into the pointer before we
293
+ // added it to the worklist. Here, in the case we are allowing non-trivial
294
+ // stores, check if the store's source is lexical, if so return false.
295
+ // Deleting a dead object with non-trivial stores, will need compensating
296
+ // destroys at the store for it's source, which will shorten the lifetime of
297
+ // the store's source.
272
298
if (auto *store = dyn_cast<StoreInst>(Inst)) {
273
- // TODO: when we have OSSA, we can also accept stores of non trivial values:
274
- // just replace the store with a destroy_value.
275
- return !onlyAcceptTrivialStores ||
276
- store->getSrc ()->getType (). isTrivial (*store-> getFunction ( ));
299
+ auto storeSrc = store-> getSrc ();
300
+ return storeSrc-> getType (). isTrivial (* store-> getFunction ()) ||
301
+ ( !onlyAcceptTrivialStores &&
302
+ (! store->getFunction ()->hasOwnership () || !storeSrc-> isLexical () ));
277
303
}
278
304
279
305
// Conceptually this instruction has no side-effects.
@@ -382,7 +408,7 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
382
408
LLVM_DEBUG (llvm::dbgs () << " Analyzing Use Graph." );
383
409
384
410
SmallVector<RefElementAddrInst *, 8 > refElementAddrs;
385
- bool deallocationMaybeInlined = false ;
411
+
386
412
BuiltinInst *destroyArray = nullptr ;
387
413
auto *allocRef = dyn_cast<AllocRefInstBase>(allocation);
388
414
@@ -396,14 +422,10 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
396
422
if (Users && !Users->insert (I)) {
397
423
LLVM_DEBUG (llvm::dbgs () << " Already seen skipping...\n " );
398
424
continue ;
399
- }
400
-
401
- if (auto *rea = dyn_cast<RefElementAddrInst>(I)) {
402
- if (!rea->getType ().isTrivial (*rea->getFunction ()))
425
+ } else if (auto *rea = dyn_cast<RefElementAddrInst>(I)) {
426
+ if (rea != allocation && !rea->getType ().isTrivial (*rea->getFunction ()))
403
427
refElementAddrs.push_back (rea);
404
- } else if (isa<SetDeallocatingInst>(I)) {
405
- deallocationMaybeInlined = true ;
406
- } else if (allocRef && Users && isDestroyArray (I)) {
428
+ } else if (allocRef && isDestroyArray (I)) {
407
429
if (destroyArray)
408
430
return true ;
409
431
destroyArray = cast<BuiltinInst>(I);
@@ -436,18 +458,14 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
436
458
}
437
459
}
438
460
439
- // In OSSA, we don't have to do this check. We can always accept a
440
- // destroyArray and insert the compensating destroys right at the store
441
- // instructions.
442
- if (destroyArray)
443
- return !onlyStoresToTailObjects (destroyArray, *Users, allocRef);
444
-
445
- if (deallocationMaybeInlined) {
446
- // The alloc_ref is not destructed by a strong_release which is calling the
447
- // deallocator (destroying all stored properties).
461
+ if (!allocation->getFunction ()->hasOwnership ()) {
462
+ // In non-ossa, if we found a destroy array builtin that destroys the tail
463
+ // elements, ensure all stores are to the taile elems.
464
+ if (destroyArray) {
465
+ return !onlyStoresToTailObjects (destroyArray, *Users, allocRef);
466
+ }
448
467
// In non-OSSA we cannot reliably track the lifetime of non-trivial stored
449
468
// properties. Removing the dead alloc_ref might leak a property value.
450
- // TODO: in OSSA we can replace stores to properties with a destroy_value.
451
469
for (RefElementAddrInst *rea : refElementAddrs) {
452
470
// Re-run the check with not accepting non-trivial stores.
453
471
if (hasUnremovableUsers (rea, nullptr , acceptRefCountInsts,
@@ -567,9 +585,8 @@ recursivelyCollectInteriorUses(ValueBase *DefInst,
567
585
auto User = Op->getUser ();
568
586
569
587
// Lifetime endpoints that don't allow the address to escape.
570
- if (isa<RefCountingInst>(User) ||
571
- isa<DebugValueInst>(User) ||
572
- isa<FixLifetimeInst>(User)) {
588
+ if (isa<RefCountingInst>(User) || isa<DebugValueInst>(User) ||
589
+ isa<FixLifetimeInst>(User) || isa<DestroyValueInst>(User)) {
573
590
AllUsers.insert (User);
574
591
continue ;
575
592
}
@@ -704,10 +721,7 @@ static void insertReleases(ArrayRef<StoreInst*> Stores,
704
721
// per block, and all release points occur after all stores. Therefore we
705
722
// can simply ask SSAUpdater for the reaching store.
706
723
SILValue RelVal = SSAUp.getValueAtEndOfBlock (RelPoint->getParent ());
707
- if (StVal->getType ().isReferenceCounted (RelPoint->getModule ()))
708
- B.createStrongRelease (Loc, RelVal, B.getDefaultAtomicity ());
709
- else
710
- B.createReleaseValue (Loc, RelVal, B.getDefaultAtomicity ());
724
+ B.emitDestroyValueOperation (Loc, RelVal);
711
725
}
712
726
}
713
727
@@ -751,10 +765,11 @@ class DeadObjectElimination : public SILFunctionTransform {
751
765
DeadEndBlocks DEBlocks (&Fn);
752
766
DestructorAnalysisCache.clear ();
753
767
768
+ LLVM_DEBUG (llvm::dbgs () << " Processing " << Fn.getName () << " \n " );
769
+
754
770
bool Changed = false ;
755
771
756
772
for (auto &BB : Fn) {
757
-
758
773
for (SILInstruction &inst : BB.deletableInstructions ()) {
759
774
if (auto *A = dyn_cast<AllocRefInstBase>(&inst))
760
775
Changed |= processAllocRef (A);
@@ -773,10 +788,6 @@ class DeadObjectElimination : public SILFunctionTransform {
773
788
}
774
789
775
790
void run () override {
776
- // FIXME: We should support ownership eventually.
777
- if (getFunction ()->hasOwnership ())
778
- return ;
779
-
780
791
assert (!domInfo);
781
792
782
793
if (processFunction (*getFunction ())) {
@@ -829,21 +840,48 @@ bool DeadObjectElimination::processAllocRef(AllocRefInstBase *ARI) {
829
840
return false ;
830
841
}
831
842
832
- // Find the instruction which releases the object's tail elements.
833
- SILInstruction *releaseOfTailElems = nullptr ;
834
- for (SILInstruction *user : UsersToRemove) {
835
- if (isDestroyArray (user) ||
836
- (destructorEffects == DestructorEffects::DestroysTailElems &&
837
- isa<RefCountingInst>(user) && user->mayRelease ())) {
838
- // Bail if we find multiple such instructions.
839
- if (releaseOfTailElems)
843
+ if (!ARI->getFunction ()->hasOwnership ()) {
844
+ // Find the instruction which releases the object's tail elements.
845
+ SILInstruction *releaseOfTailElems = nullptr ;
846
+ for (SILInstruction *user : UsersToRemove) {
847
+ if (isDestroyArray (user) ||
848
+ (destructorEffects == DestructorEffects::DestroysTailElems &&
849
+ isa<RefCountingInst>(user) && user->mayRelease ())) {
850
+ // Bail if we find multiple such instructions.
851
+ if (releaseOfTailElems)
852
+ return false ;
853
+ releaseOfTailElems = user;
854
+ }
855
+ }
856
+ if (releaseOfTailElems) {
857
+ if (!insertCompensatingReleases (releaseOfTailElems, UsersToRemove)) {
840
858
return false ;
841
- releaseOfTailElems = user;
859
+ }
842
860
}
843
861
}
844
- if (releaseOfTailElems) {
845
- if (!insertCompensatingReleases (releaseOfTailElems, UsersToRemove)) {
846
- return false ;
862
+
863
+ if (ARI->getFunction ()->hasOwnership ()) {
864
+ // In ossa, we are going to delete the dead element store and insert a
865
+ // destroy_value of the store's source. This is shortening the store's
866
+ // source lifetime. Check if there was a pointer escape of the store's
867
+ // source, if so bail out.
868
+ for (auto *user : UsersToRemove) {
869
+ auto *store = dyn_cast<StoreInst>(user);
870
+ if (!store ||
871
+ store->getOwnershipQualifier () == StoreOwnershipQualifier::Trivial)
872
+ continue ;
873
+ if (findPointerEscape (store->getSrc ())) {
874
+ return false ;
875
+ }
876
+ }
877
+ for (auto *user : UsersToRemove) {
878
+ auto *store = dyn_cast<StoreInst>(user);
879
+ if (!store ||
880
+ store->getOwnershipQualifier () == StoreOwnershipQualifier::Trivial) {
881
+ continue ;
882
+ }
883
+ SILBuilderWithScope (store).createDestroyValue (store->getLoc (),
884
+ store->getSrc ());
847
885
}
848
886
}
849
887
@@ -859,13 +897,41 @@ bool DeadObjectElimination::processAllocRef(AllocRefInstBase *ARI) {
859
897
bool DeadObjectElimination::processAllocStack (AllocStackInst *ASI) {
860
898
// Trivial types don't have destructors.
861
899
bool isTrivialType = ASI->getElementType ().isTrivial (*ASI->getFunction ());
900
+ // In non-ossa, only accept trivial stores if we have a non-trivial
901
+ // alloc_stack
902
+ bool onlyAcceptTrivialStores =
903
+ ASI->getFunction ()->hasOwnership () ? false : !isTrivialType;
862
904
UserList UsersToRemove;
863
- if (hasUnremovableUsers (ASI, &UsersToRemove, /* acceptRefCountInsts=*/ true ,
864
- /* onlyAcceptTrivialStores*/ !isTrivialType )) {
905
+ if (hasUnremovableUsers (ASI, &UsersToRemove, /* acceptRefCountInsts=*/ true ,
906
+ onlyAcceptTrivialStores)) {
865
907
LLVM_DEBUG (llvm::dbgs () << " Found a use that cannot be zapped...\n " );
866
908
return false ;
867
909
}
868
910
911
+ if (ASI->getFunction ()->hasOwnership ()) {
912
+ for (auto *user : UsersToRemove) {
913
+ auto *store = dyn_cast<StoreInst>(user);
914
+ if (!store ||
915
+ store->getOwnershipQualifier () == StoreOwnershipQualifier::Trivial)
916
+ continue ;
917
+ // In ossa, we are going to delete the dead store and insert a
918
+ // destroy_value of the store's source. This is shortening the store's
919
+ // source lifetime. Check if there was a pointer escape of the store's
920
+ // source, if so bail out.
921
+ if (findPointerEscape (store->getSrc ())) {
922
+ return false ;
923
+ }
924
+ }
925
+ for (auto *user : UsersToRemove) {
926
+ auto *store = dyn_cast<StoreInst>(user);
927
+ if (!store ||
928
+ store->getOwnershipQualifier () == StoreOwnershipQualifier::Trivial)
929
+ continue ;
930
+ SILBuilderWithScope (store).createDestroyValue (store->getLoc (),
931
+ store->getSrc ());
932
+ }
933
+ }
934
+
869
935
// Remove the AllocRef and all of its users.
870
936
removeInstructions (
871
937
ArrayRef<SILInstruction*>(UsersToRemove.begin (), UsersToRemove.end ()));
@@ -883,11 +949,38 @@ bool DeadObjectElimination::processKeyPath(KeyPathInst *KPI) {
883
949
return false ;
884
950
}
885
951
886
- // For simplicity just bail if the keypath has a non-trivial operands.
887
- // TODO: don't bail but insert compensating destroys for such operands.
952
+ bool hasOwnership = KPI->getFunction ()->hasOwnership ();
888
953
for (const Operand &Op : KPI->getPatternOperands ()) {
889
- if (!Op.get ()->getType ().isTrivial (*KPI->getFunction ()))
954
+ // In non-ossa, bail out if we have non-trivial pattern operands.
955
+ if (!hasOwnership) {
956
+ if (Op.get ()->getType ().isTrivial (*KPI->getFunction ()))
957
+ return false ;
958
+ continue ;
959
+ }
960
+ // In ossa, bail out if we have non-trivial pattern operand values that are
961
+ // lexical.
962
+ if (Op.get ()->isLexical ()) {
890
963
return false ;
964
+ }
965
+ }
966
+
967
+ if (KPI->getFunction ()->hasOwnership ()) {
968
+ for (const Operand &Op : KPI->getPatternOperands ()) {
969
+ if (Op.get ()->getType ().isTrivial (*KPI->getFunction ()))
970
+ continue ;
971
+ // In ossa, we are going to delete the dead keypath which was consuming
972
+ // the pattern operand and insert a destroy_value of the pattern operand
973
+ // value. This is shortening the pattern operand value's lifetime. Check
974
+ // if there was a pointer escape, if so bail out.
975
+ if (findPointerEscape (Op.get ())) {
976
+ return false ;
977
+ }
978
+ }
979
+ for (const Operand &Op : KPI->getPatternOperands ()) {
980
+ if (Op.get ()->getType ().isTrivial (*KPI->getFunction ()))
981
+ continue ;
982
+ SILBuilderWithScope (KPI).createDestroyValue (KPI->getLoc (), Op.get ());
983
+ }
891
984
}
892
985
893
986
// Remove the keypath and all of its users.
@@ -924,10 +1017,11 @@ bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved(
924
1017
925
1018
if (auto *ARI = dyn_cast<AllocRefInstBase>(Arg0)) {
926
1019
if (all_of (ARI->getUses (), [&](Operand *Op) -> bool {
927
- if (Op->getUser () == AI)
1020
+ auto *user = Op->getUser ();
1021
+ if (user == AI)
928
1022
return true ;
929
- if (auto *SRI = dyn_cast <StrongReleaseInst>(Op-> getUser () )) {
930
- ToDestroy.emplace_back (SRI );
1023
+ if (isa <StrongReleaseInst>(user) || isa<DestroyValueInst>(user )) {
1024
+ ToDestroy.emplace_back (user );
931
1025
return true ;
932
1026
}
933
1027
return false ;
@@ -955,19 +1049,33 @@ bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved(
955
1049
// or we could also handle calls to array.init.
956
1050
bool DeadObjectElimination::removeAndReleaseArray (
957
1051
SingleValueInstruction *NewArrayValue, DeadEndBlocks &DEBlocks) {
958
- TupleExtractInst *ArrayDef = nullptr ;
959
- TupleExtractInst *StorageAddress = nullptr ;
960
- for (auto *Op : NewArrayValue->getUses ()) {
961
- auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser ());
962
- if (!TupleElt)
1052
+ SILValue ArrayDef = nullptr ;
1053
+ SILValue StorageAddress = nullptr ;
1054
+
1055
+ if (NewArrayValue->getFunction ()->hasOwnership ()) {
1056
+ auto *destructureTuple =
1057
+ NewArrayValue->getSingleConsumingUserOfType <DestructureTupleInst>();
1058
+ if (!destructureTuple) {
963
1059
return false ;
964
- if (TupleElt->getFieldIndex () == 0 && !ArrayDef) {
965
- ArrayDef = TupleElt;
966
- } else if (TupleElt->getFieldIndex () == 1 && !StorageAddress) {
967
- StorageAddress = TupleElt;
968
- } else {
1060
+ }
1061
+ if (destructureTuple->getNumResults () != 2 ) {
969
1062
return false ;
970
1063
}
1064
+ ArrayDef = destructureTuple->getResult (0 );
1065
+ StorageAddress = destructureTuple->getResult (1 );
1066
+ } else {
1067
+ for (auto *Op : NewArrayValue->getUses ()) {
1068
+ auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser ());
1069
+ if (!TupleElt)
1070
+ return false ;
1071
+ if (TupleElt->getFieldIndex () == 0 && !ArrayDef) {
1072
+ ArrayDef = TupleElt;
1073
+ } else if (TupleElt->getFieldIndex () == 1 && !StorageAddress) {
1074
+ StorageAddress = TupleElt;
1075
+ } else {
1076
+ return false ;
1077
+ }
1078
+ }
971
1079
}
972
1080
if (!ArrayDef)
973
1081
return false ; // No Array object to delete.
0 commit comments