@@ -62,6 +62,19 @@ STATISTIC(DeadAllocApplyEliminated,
62
62
63
63
using UserList = llvm::SmallSetVector<SILInstruction *, 16 >;
64
64
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
+
65
78
// Analyzing the body of this class destructor is valid because the object is
66
79
// dead. This means that the object is never passed to objc_setAssociatedObject,
67
80
// so its destructor cannot be extended at runtime.
@@ -98,14 +111,21 @@ static SILFunction *getDestructor(AllocRefInst *ARI) {
98
111
return Fn;
99
112
}
100
113
114
+ static bool isDestroyArray (SILInstruction *inst) {
115
+ BuiltinInst *bi = dyn_cast<BuiltinInst>(inst);
116
+ return bi && bi->getBuiltinInfo ().ID == BuiltinValueKind::DestroyArray;
117
+ }
118
+
101
119
// / Analyze the destructor for the class of ARI to see if any instructions in it
102
120
// / could have side effects on the program outside the destructor. If it does
103
121
// / not, then we can eliminate the destructor.
104
- static bool doesDestructorHaveSideEffects (AllocRefInst *ARI) {
122
+ static DestructorEffects doesDestructorHaveSideEffects (AllocRefInst *ARI) {
105
123
SILFunction *Fn = getDestructor (ARI);
106
124
// If we can't find a constructor then assume it has side effects.
107
125
if (!Fn)
108
- return true ;
126
+ return DestructorEffects::Unknown;
127
+
128
+ DestructorEffects effects = DestructorEffects::None;
109
129
110
130
// A destructor only has one argument, self.
111
131
assert (Fn->begin ()->getNumArguments () == 1 &&
@@ -115,7 +135,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
115
135
LLVM_DEBUG (llvm::dbgs () << " Analyzing destructor.\n " );
116
136
117
137
// For each BB in the destructor...
118
- for (auto &BB : *Fn)
138
+ for (auto &BB : *Fn) {
119
139
// For each instruction I in BB...
120
140
for (auto &I : BB) {
121
141
LLVM_DEBUG (llvm::dbgs () << " Visiting: " << I);
@@ -127,6 +147,14 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
127
147
continue ;
128
148
}
129
149
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
+
130
158
// RefCounting operations on Self are ok since we are already in the
131
159
// destructor. RefCountingOperations on other instructions could have side
132
160
// effects though.
@@ -142,7 +170,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
142
170
} else {
143
171
LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Ref count operation "
144
172
" not on self.\n " );
145
- return true ;
173
+ return DestructorEffects::Unknown ;
146
174
}
147
175
}
148
176
@@ -162,7 +190,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
162
190
} else {
163
191
LLVM_DEBUG (llvm::dbgs () << " UNSAFE! dealloc_ref on value "
164
192
" besides self.\n " );
165
- return true ;
193
+ return DestructorEffects::Unknown ;
166
194
}
167
195
}
168
196
@@ -174,13 +202,28 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
174
202
continue ;
175
203
}
176
204
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
+
177
220
LLVM_DEBUG (llvm::dbgs () << " UNSAFE! Unknown instruction.\n " );
178
221
// Otherwise, we can't remove the deallocation completely.
179
- return true ;
222
+ return DestructorEffects::Unknown ;
180
223
}
181
-
224
+ }
182
225
// We didn't find any side effects.
183
- return false ;
226
+ return effects ;
184
227
}
185
228
186
229
// ===----------------------------------------------------------------------===//
@@ -317,11 +360,6 @@ static bool onlyStoresToTailObjects(BuiltinInst *destroyArray,
317
360
return true ;
318
361
}
319
362
320
- static bool isDestroyArray (SILInstruction *inst) {
321
- BuiltinInst *bi = dyn_cast<BuiltinInst>(inst);
322
- return bi && bi->getBuiltinInfo ().ID == BuiltinValueKind::DestroyArray;
323
- }
324
-
325
363
// / Inserts releases of all stores in \p users.
326
364
static void insertCompensatingReleases (SILInstruction *before,
327
365
const UserList &users) {
@@ -424,7 +462,6 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users,
424
462
// NonTrivial DeadObject Elimination
425
463
// ===----------------------------------------------------------------------===//
426
464
427
- namespace {
428
465
// / Determine if an object is dead. Compute its original lifetime. Find the
429
466
// / lifetime endpoints reached by each store of a refcounted object into the
430
467
// / object.
@@ -687,7 +724,8 @@ static bool isAllocatingApply(SILInstruction *Inst) {
687
724
688
725
namespace {
689
726
class DeadObjectElimination : public SILFunctionTransform {
690
- llvm::DenseMap<SILType, bool > DestructorAnalysisCache;
727
+
728
+ llvm::DenseMap<SILType, DestructorEffects> DestructorAnalysisCache;
691
729
692
730
InstructionDeleter deleter;
693
731
@@ -754,12 +792,12 @@ DeadObjectElimination::removeInstructions(ArrayRef<SILInstruction*> toRemove) {
754
792
bool DeadObjectElimination::processAllocRef (AllocRefInst *ARI) {
755
793
// Ok, we have an alloc_ref. Check the cache to see if we have already
756
794
// computed the destructor behavior for its SILType.
757
- bool HasSideEffects ;
795
+ DestructorEffects destructorEffects ;
758
796
SILType Type = ARI->getType ();
759
797
auto CacheSearchResult = DestructorAnalysisCache.find (Type);
760
798
if (CacheSearchResult != DestructorAnalysisCache.end ()) {
761
799
// Ok we found a value in the cache.
762
- HasSideEffects = CacheSearchResult->second ;
800
+ destructorEffects = CacheSearchResult->second ;
763
801
} else {
764
802
// We did not find a value in the cache for our destructor. Analyze the
765
803
// destructor to make sure it has no side effects. For now this only
@@ -769,24 +807,34 @@ bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) {
769
807
//
770
808
// TODO: We should be able to handle destructors that do nothing but release
771
809
// members of the object.
772
- HasSideEffects = doesDestructorHaveSideEffects (ARI);
773
- DestructorAnalysisCache[Type] = HasSideEffects ;
810
+ destructorEffects = doesDestructorHaveSideEffects (ARI);
811
+ DestructorAnalysisCache[Type] = destructorEffects ;
774
812
}
775
813
776
814
// Our destructor has no side effects, so if we can prove that no loads
777
815
// escape, then we can completely remove the use graph of this alloc_ref.
778
816
UserList UsersToRemove;
779
817
if (hasUnremovableUsers (ARI, &UsersToRemove,
780
- /* acceptRefCountInsts=*/ !HasSideEffects ,
781
- /* onlyAcceptTrivialStores*/ false )) {
818
+ /* acceptRefCountInsts=*/ destructorEffects != DestructorEffects::Unknown ,
819
+ /* onlyAcceptTrivialStores*/ false )) {
782
820
LLVM_DEBUG (llvm::dbgs () << " Found a use that cannot be zapped...\n " );
783
821
return false ;
784
822
}
785
823
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);
790
838
791
839
// Remove the AllocRef and all of its users.
792
840
removeInstructions (
0 commit comments