@@ -62,6 +62,19 @@ STATISTIC(DeadAllocApplyEliminated,
6262
6363using UserList = llvm::SmallSetVector<SILInstruction *, 16 >;
6464
65+ namespace {
66+
67+ // / Side effects of a destructor.
68+ enum class DestructorEffects {
69+ None,
70+
71+ // / The destructor contains a "destroyArray" builtin which destroys the tail
72+ // / elements of the object - like in Array.
73+ DestroysTailElems,
74+
75+ Unknown
76+ };
77+
6578// Analyzing the body of this class destructor is valid because the object is
6679// dead. This means that the object is never passed to objc_setAssociatedObject,
6780// so its destructor cannot be extended at runtime.
@@ -98,14 +111,21 @@ static SILFunction *getDestructor(AllocRefInst *ARI) {
98111 return Fn;
99112}
100113
114+ static bool isDestroyArray (SILInstruction *inst) {
115+ BuiltinInst *bi = dyn_cast<BuiltinInst>(inst);
116+ return bi && bi->getBuiltinInfo ().ID == BuiltinValueKind::DestroyArray;
117+ }
118+
101119// / Analyze the destructor for the class of ARI to see if any instructions in it
102120// / could have side effects on the program outside the destructor. If it does
103121// / not, then we can eliminate the destructor.
104- static bool doesDestructorHaveSideEffects (AllocRefInst *ARI) {
122+ static DestructorEffects doesDestructorHaveSideEffects (AllocRefInst *ARI) {
105123 SILFunction *Fn = getDestructor (ARI);
106124 // If we can't find a constructor then assume it has side effects.
107125 if (!Fn)
108- return true ;
126+ return DestructorEffects::Unknown;
127+
128+ DestructorEffects effects = DestructorEffects::None;
109129
110130 // A destructor only has one argument, self.
111131 assert (Fn->begin ()->getNumArguments () == 1 &&
@@ -115,7 +135,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
115135 LLVM_DEBUG (llvm::dbgs () << " Analyzing destructor.\n " );
116136
117137 // For each BB in the destructor...
118- for (auto &BB : *Fn)
138+ for (auto &BB : *Fn) {
119139 // For each instruction I in BB...
120140 for (auto &I : BB) {
121141 LLVM_DEBUG (llvm::dbgs () << " Visiting: " << I);
@@ -127,6 +147,14 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
127147 continue ;
128148 }
129149
150+ if (auto *fl = dyn_cast<FixLifetimeInst>(&I)) {
151+ // A fix_lifetime of self does cannot have a side effect, because in the
152+ // destructor, Self is deleted.
153+ if (stripCasts (fl->getOperand ()) == Self)
154+ continue ;
155+ return DestructorEffects::Unknown;
156+ }
157+
130158 // RefCounting operations on Self are ok since we are already in the
131159 // destructor. RefCountingOperations on other instructions could have side
132160 // effects though.
@@ -142,7 +170,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
142170 } else {
143171 LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
144172 " not on self.\n " );
145- return true ;
173+ return DestructorEffects::Unknown ;
146174 }
147175 }
148176
@@ -162,7 +190,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
162190 } else {
163191 LLVM_DEBUG (llvm::dbgs () << " UNSAFE! dealloc_ref on value "
164192 " besides self.\n " );
165- return true ;
193+ return DestructorEffects::Unknown ;
166194 }
167195 }
168196
@@ -174,13 +202,28 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
174202 continue ;
175203 }
176204
205+ if (isDestroyArray (&I)) {
206+ // Check if the "destroyArray" destroys the tail elements of the object,
207+ // like in Array.
208+ SILValue addr = I.getOperand (1 );
209+ auto *atp = dyn_cast<AddressToPointerInst>(addr);
210+ if (!atp)
211+ return DestructorEffects::Unknown;
212+ auto *rta = dyn_cast<RefTailAddrInst>(atp->getOperand ());
213+ if (!rta)
214+ return DestructorEffects::Unknown;
215+ effects = DestructorEffects::DestroysTailElems;
216+ if (rta->getOperand () == Self)
217+ continue ;
218+ }
219+
177220 LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Unknown instruction.\n " );
178221 // Otherwise, we can't remove the deallocation completely.
179- return true ;
222+ return DestructorEffects::Unknown ;
180223 }
181-
224+ }
182225 // We didn't find any side effects.
183- return false ;
226+ return effects ;
184227}
185228
186229// ===----------------------------------------------------------------------===//
@@ -317,11 +360,6 @@ static bool onlyStoresToTailObjects(BuiltinInst *destroyArray,
317360 return true ;
318361}
319362
320- static bool isDestroyArray (SILInstruction *inst) {
321- BuiltinInst *bi = dyn_cast<BuiltinInst>(inst);
322- return bi && bi->getBuiltinInfo ().ID == BuiltinValueKind::DestroyArray;
323- }
324-
325363// / Inserts releases of all stores in \p users.
326364static void insertCompensatingReleases (SILInstruction *before,
327365 const UserList &users) {
@@ -424,7 +462,6 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
424462// NonTrivial DeadObject Elimination
425463// ===----------------------------------------------------------------------===//
426464
427- namespace {
428465// / Determine if an object is dead. Compute its original lifetime. Find the
429466// / lifetime endpoints reached by each store of a refcounted object into the
430467// / object.
@@ -687,7 +724,8 @@ static bool isAllocatingApply(SILInstruction *Inst) {
687724
688725namespace {
689726class DeadObjectElimination : public SILFunctionTransform {
690- llvm::DenseMap<SILType, bool > DestructorAnalysisCache;
727+
728+ llvm::DenseMap<SILType, DestructorEffects> DestructorAnalysisCache;
691729
692730 InstructionDeleter deleter;
693731
@@ -754,12 +792,12 @@ DeadObjectElimination::removeInstructions(ArrayRef<SILInstruction*> toRemove) {
754792bool DeadObjectElimination::processAllocRef (AllocRefInst *ARI) {
755793 // Ok, we have an alloc_ref. Check the cache to see if we have already
756794 // computed the destructor behavior for its SILType.
757- bool HasSideEffects ;
795+ DestructorEffects destructorEffects ;
758796 SILType Type = ARI->getType ();
759797 auto CacheSearchResult = DestructorAnalysisCache.find (Type);
760798 if (CacheSearchResult != DestructorAnalysisCache.end ()) {
761799 // Ok we found a value in the cache.
762- HasSideEffects = CacheSearchResult->second ;
800+ destructorEffects = CacheSearchResult->second ;
763801 } else {
764802 // We did not find a value in the cache for our destructor. Analyze the
765803 // destructor to make sure it has no side effects. For now this only
@@ -769,24 +807,34 @@ bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) {
769807 //
770808 // TODO: We should be able to handle destructors that do nothing but release
771809 // members of the object.
772- HasSideEffects = doesDestructorHaveSideEffects (ARI);
773- DestructorAnalysisCache[Type] = HasSideEffects ;
810+ destructorEffects = doesDestructorHaveSideEffects (ARI);
811+ DestructorAnalysisCache[Type] = destructorEffects ;
774812 }
775813
776814 // Our destructor has no side effects, so if we can prove that no loads
777815 // escape, then we can completely remove the use graph of this alloc_ref.
778816 UserList UsersToRemove;
779817 if (hasUnremovableUsers (ARI, &UsersToRemove,
780- /* acceptRefCountInsts=*/ !HasSideEffects ,
781- /* onlyAcceptTrivialStores*/ false )) {
818+ /* acceptRefCountInsts=*/ destructorEffects != DestructorEffects::Unknown ,
819+ /* onlyAcceptTrivialStores*/ false )) {
782820 LLVM_DEBUG (llvm::dbgs () << " Found a use that cannot be zapped...\n " );
783821 return false ;
784822 }
785823
786- auto iter = std::find_if (UsersToRemove.begin (), UsersToRemove.end (),
787- isDestroyArray);
788- if (iter != UsersToRemove.end ())
789- insertCompensatingReleases (*iter, UsersToRemove);
824+ // Find the instruction which releases the object's tail elements.
825+ SILInstruction *releaseOfTailElems = nullptr ;
826+ for (SILInstruction *user : UsersToRemove) {
827+ if (isDestroyArray (user) ||
828+ (destructorEffects == DestructorEffects::DestroysTailElems &&
829+ isa<RefCountingInst>(user) && user->mayRelease ())) {
830+ // Bail if we find multiple such instructions.
831+ if (releaseOfTailElems)
832+ return false ;
833+ releaseOfTailElems = user;
834+ }
835+ }
836+ if (releaseOfTailElems)
837+ insertCompensatingReleases (releaseOfTailElems, UsersToRemove);
790838
791839 // Remove the AllocRef and all of its users.
792840 removeInstructions (
0 commit comments