2929#include " swift/SIL/BasicBlockUtils.h"
3030#include " swift/SIL/DebugUtils.h"
3131#include " swift/SIL/InstructionUtils.h"
32+ #include " swift/SIL/OwnershipUtils.h"
3233#include " swift/SIL/Projection.h"
3334#include " swift/SIL/SILArgument.h"
3435#include " swift/SIL/SILDeclRef.h"
@@ -127,6 +128,10 @@ static bool isDestroyArray(SILInstruction *inst) {
127128// / Analyze the destructor for the class of ARI to see if any instructions in it
128129// / could have side effects on the program outside the destructor. If it does
129130// / 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.
130135static DestructorEffects doesDestructorHaveSideEffects (AllocRefInstBase *ARI) {
131136 SILFunction *Fn = getDestructor (ARI);
132137 // If we can't find a constructor then assume it has side effects.
@@ -173,13 +178,22 @@ static DestructorEffects doesDestructorHaveSideEffects(AllocRefInstBase *ARI) {
173178 assert (RefInst->getNumOperands () == 1 &&
174179 " Make sure RefInst only has one argument." );
175180 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 " );
177192 continue ;
178- } else {
179- LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
180- " not on self.\n " );
181- return DestructorEffects::Unknown;
182193 }
194+ LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
195+ " not on self.\n " );
196+ return DestructorEffects::Unknown;
183197 }
184198
185199 // dealloc_stack can be ignored.
@@ -242,7 +256,15 @@ static DestructorEffects doesDestructorHaveSideEffects(AllocRefInstBase *ARI) {
242256// / alloc_ref alive.
243257static bool canZapInstruction (SILInstruction *Inst, bool acceptRefCountInsts,
244258 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))
246268 return true ;
247269
248270 // It is ok to eliminate various retains/releases. We are either removing
@@ -267,13 +289,17 @@ static bool canZapInstruction(SILInstruction *Inst, bool acceptRefCountInsts,
267289 if (isa<DestroyAddrInst>(Inst))
268290 return true ;
269291
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.
272298 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 () ));
277303 }
278304
279305 // Conceptually this instruction has no side-effects.
@@ -382,7 +408,7 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
382408 LLVM_DEBUG (llvm::dbgs () << " Analyzing Use Graph." );
383409
384410 SmallVector<RefElementAddrInst *, 8 > refElementAddrs;
385- bool deallocationMaybeInlined = false ;
411+
386412 BuiltinInst *destroyArray = nullptr ;
387413 auto *allocRef = dyn_cast<AllocRefInstBase>(allocation);
388414
@@ -396,14 +422,10 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
396422 if (Users && !Users->insert (I)) {
397423 LLVM_DEBUG (llvm::dbgs () << " Already seen skipping...\n " );
398424 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 ()))
403427 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)) {
407429 if (destroyArray)
408430 return true ;
409431 destroyArray = cast<BuiltinInst>(I);
@@ -436,18 +458,14 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
436458 }
437459 }
438460
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+ }
448467 // In non-OSSA we cannot reliably track the lifetime of non-trivial stored
449468 // 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.
451469 for (RefElementAddrInst *rea : refElementAddrs) {
452470 // Re-run the check with not accepting non-trivial stores.
453471 if (hasUnremovableUsers (rea, nullptr , acceptRefCountInsts,
@@ -567,9 +585,8 @@ recursivelyCollectInteriorUses(ValueBase *DefInst,
567585 auto User = Op->getUser ();
568586
569587 // 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)) {
573590 AllUsers.insert (User);
574591 continue ;
575592 }
@@ -704,10 +721,7 @@ static void insertReleases(ArrayRef<StoreInst*> Stores,
704721 // per block, and all release points occur after all stores. Therefore we
705722 // can simply ask SSAUpdater for the reaching store.
706723 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);
711725 }
712726}
713727
@@ -751,10 +765,11 @@ class DeadObjectElimination : public SILFunctionTransform {
751765 DeadEndBlocks DEBlocks (&Fn);
752766 DestructorAnalysisCache.clear ();
753767
768+ LLVM_DEBUG (llvm::dbgs () << " Processing " << Fn.getName () << " \n " );
769+
754770 bool Changed = false ;
755771
756772 for (auto &BB : Fn) {
757-
758773 for (SILInstruction &inst : BB.deletableInstructions ()) {
759774 if (auto *A = dyn_cast<AllocRefInstBase>(&inst))
760775 Changed |= processAllocRef (A);
@@ -773,10 +788,6 @@ class DeadObjectElimination : public SILFunctionTransform {
773788 }
774789
775790 void run () override {
776- // FIXME: We should support ownership eventually.
777- if (getFunction ()->hasOwnership ())
778- return ;
779-
780791 assert (!domInfo);
781792
782793 if (processFunction (*getFunction ())) {
@@ -829,21 +840,48 @@ bool DeadObjectElimination::processAllocRef(AllocRefInstBase *ARI) {
829840 return false ;
830841 }
831842
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)) {
840858 return false ;
841- releaseOfTailElems = user;
859+ }
842860 }
843861 }
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 ());
847885 }
848886 }
849887
@@ -859,13 +897,41 @@ bool DeadObjectElimination::processAllocRef(AllocRefInstBase *ARI) {
859897bool DeadObjectElimination::processAllocStack (AllocStackInst *ASI) {
860898 // Trivial types don't have destructors.
861899 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;
862904 UserList UsersToRemove;
863- if (hasUnremovableUsers (ASI, &UsersToRemove, /* acceptRefCountInsts=*/ true ,
864- /* onlyAcceptTrivialStores*/ !isTrivialType )) {
905+ if (hasUnremovableUsers (ASI, &UsersToRemove, /* acceptRefCountInsts=*/ true ,
906+ onlyAcceptTrivialStores)) {
865907 LLVM_DEBUG (llvm::dbgs () << " Found a use that cannot be zapped...\n " );
866908 return false ;
867909 }
868910
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+
869935 // Remove the AllocRef and all of its users.
870936 removeInstructions (
871937 ArrayRef<SILInstruction*>(UsersToRemove.begin (), UsersToRemove.end ()));
@@ -883,11 +949,38 @@ bool DeadObjectElimination::processKeyPath(KeyPathInst *KPI) {
883949 return false ;
884950 }
885951
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 ();
888953 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 ()) {
890963 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+ }
891984 }
892985
893986 // Remove the keypath and all of its users.
@@ -924,10 +1017,11 @@ bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved(
9241017
9251018 if (auto *ARI = dyn_cast<AllocRefInstBase>(Arg0)) {
9261019 if (all_of (ARI->getUses (), [&](Operand *Op) -> bool {
927- if (Op->getUser () == AI)
1020+ auto *user = Op->getUser ();
1021+ if (user == AI)
9281022 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 );
9311025 return true ;
9321026 }
9331027 return false ;
@@ -955,19 +1049,33 @@ bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved(
9551049// or we could also handle calls to array.init.
9561050bool DeadObjectElimination::removeAndReleaseArray (
9571051 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) {
9631059 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 ) {
9691062 return false ;
9701063 }
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+ }
9711079 }
9721080 if (!ArrayDef)
9731081 return false ; // No Array object to delete.
0 commit comments