1 //===--- PtrState.cpp -----------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "PtrState.h"
11 #include "DependencyAnalysis.h"
12 #include "ObjCARC.h"
13 #include "llvm/Support/Debug.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 using namespace llvm;
17 using namespace llvm::objcarc;
18 
19 #define DEBUG_TYPE "objc-arc-ptr-state"
20 
21 //===----------------------------------------------------------------------===//
22 //                                  Utility
23 //===----------------------------------------------------------------------===//
24 
operator <<(raw_ostream & OS,const Sequence S)25 raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS, const Sequence S) {
26   switch (S) {
27   case S_None:
28     return OS << "S_None";
29   case S_Retain:
30     return OS << "S_Retain";
31   case S_CanRelease:
32     return OS << "S_CanRelease";
33   case S_Use:
34     return OS << "S_Use";
35   case S_Release:
36     return OS << "S_Release";
37   case S_MovableRelease:
38     return OS << "S_MovableRelease";
39   case S_Stop:
40     return OS << "S_Stop";
41   }
42   llvm_unreachable("Unknown sequence type.");
43 }
44 
45 //===----------------------------------------------------------------------===//
46 //                                  Sequence
47 //===----------------------------------------------------------------------===//
48 
MergeSeqs(Sequence A,Sequence B,bool TopDown)49 static Sequence MergeSeqs(Sequence A, Sequence B, bool TopDown) {
50   // The easy cases.
51   if (A == B)
52     return A;
53   if (A == S_None || B == S_None)
54     return S_None;
55 
56   if (A > B)
57     std::swap(A, B);
58   if (TopDown) {
59     // Choose the side which is further along in the sequence.
60     if ((A == S_Retain || A == S_CanRelease) &&
61         (B == S_CanRelease || B == S_Use))
62       return B;
63   } else {
64     // Choose the side which is further along in the sequence.
65     if ((A == S_Use || A == S_CanRelease) &&
66         (B == S_Use || B == S_Release || B == S_Stop || B == S_MovableRelease))
67       return A;
68     // If both sides are releases, choose the more conservative one.
69     if (A == S_Stop && (B == S_Release || B == S_MovableRelease))
70       return A;
71     if (A == S_Release && B == S_MovableRelease)
72       return A;
73   }
74 
75   return S_None;
76 }
77 
78 //===----------------------------------------------------------------------===//
79 //                                   RRInfo
80 //===----------------------------------------------------------------------===//
81 
clear()82 void RRInfo::clear() {
83   KnownSafe = false;
84   IsTailCallRelease = false;
85   ReleaseMetadata = nullptr;
86   Calls.clear();
87   ReverseInsertPts.clear();
88   CFGHazardAfflicted = false;
89 }
90 
Merge(const RRInfo & Other)91 bool RRInfo::Merge(const RRInfo &Other) {
92   // Conservatively merge the ReleaseMetadata information.
93   if (ReleaseMetadata != Other.ReleaseMetadata)
94     ReleaseMetadata = nullptr;
95 
96   // Conservatively merge the boolean state.
97   KnownSafe &= Other.KnownSafe;
98   IsTailCallRelease &= Other.IsTailCallRelease;
99   CFGHazardAfflicted |= Other.CFGHazardAfflicted;
100 
101   // Merge the call sets.
102   Calls.insert(Other.Calls.begin(), Other.Calls.end());
103 
104   // Merge the insert point sets. If there are any differences,
105   // that makes this a partial merge.
106   bool Partial = ReverseInsertPts.size() != Other.ReverseInsertPts.size();
107   for (Instruction *Inst : Other.ReverseInsertPts)
108     Partial |= ReverseInsertPts.insert(Inst).second;
109   return Partial;
110 }
111 
112 //===----------------------------------------------------------------------===//
113 //                                  PtrState
114 //===----------------------------------------------------------------------===//
115 
SetKnownPositiveRefCount()116 void PtrState::SetKnownPositiveRefCount() {
117   DEBUG(dbgs() << "        Setting Known Positive.\n");
118   KnownPositiveRefCount = true;
119 }
120 
ClearKnownPositiveRefCount()121 void PtrState::ClearKnownPositiveRefCount() {
122   DEBUG(dbgs() << "        Clearing Known Positive.\n");
123   KnownPositiveRefCount = false;
124 }
125 
SetSeq(Sequence NewSeq)126 void PtrState::SetSeq(Sequence NewSeq) {
127   DEBUG(dbgs() << "            Old: " << GetSeq() << "; New: " << NewSeq << "\n");
128   Seq = NewSeq;
129 }
130 
ResetSequenceProgress(Sequence NewSeq)131 void PtrState::ResetSequenceProgress(Sequence NewSeq) {
132   DEBUG(dbgs() << "        Resetting sequence progress.\n");
133   SetSeq(NewSeq);
134   Partial = false;
135   RRI.clear();
136 }
137 
Merge(const PtrState & Other,bool TopDown)138 void PtrState::Merge(const PtrState &Other, bool TopDown) {
139   Seq = MergeSeqs(GetSeq(), Other.GetSeq(), TopDown);
140   KnownPositiveRefCount &= Other.KnownPositiveRefCount;
141 
142   // If we're not in a sequence (anymore), drop all associated state.
143   if (Seq == S_None) {
144     Partial = false;
145     RRI.clear();
146   } else if (Partial || Other.Partial) {
147     // If we're doing a merge on a path that's previously seen a partial
148     // merge, conservatively drop the sequence, to avoid doing partial
149     // RR elimination. If the branch predicates for the two merge differ,
150     // mixing them is unsafe.
151     ClearSequenceProgress();
152   } else {
153     // Otherwise merge the other PtrState's RRInfo into our RRInfo. At this
154     // point, we know that currently we are not partial. Stash whether or not
155     // the merge operation caused us to undergo a partial merging of reverse
156     // insertion points.
157     Partial = RRI.Merge(Other.RRI);
158   }
159 }
160 
161 //===----------------------------------------------------------------------===//
162 //                              BottomUpPtrState
163 //===----------------------------------------------------------------------===//
164 
InitBottomUp(ARCMDKindCache & Cache,Instruction * I)165 bool BottomUpPtrState::InitBottomUp(ARCMDKindCache &Cache, Instruction *I) {
166   // If we see two releases in a row on the same pointer. If so, make
167   // a note, and we'll cicle back to revisit it after we've
168   // hopefully eliminated the second release, which may allow us to
169   // eliminate the first release too.
170   // Theoretically we could implement removal of nested retain+release
171   // pairs by making PtrState hold a stack of states, but this is
172   // simple and avoids adding overhead for the non-nested case.
173   bool NestingDetected = false;
174   if (GetSeq() == S_Release || GetSeq() == S_MovableRelease) {
175     DEBUG(dbgs() << "        Found nested releases (i.e. a release pair)\n");
176     NestingDetected = true;
177   }
178 
179   MDNode *ReleaseMetadata =
180       I->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));
181   Sequence NewSeq = ReleaseMetadata ? S_MovableRelease : S_Release;
182   ResetSequenceProgress(NewSeq);
183   SetReleaseMetadata(ReleaseMetadata);
184   SetKnownSafe(HasKnownPositiveRefCount());
185   SetTailCallRelease(cast<CallInst>(I)->isTailCall());
186   InsertCall(I);
187   SetKnownPositiveRefCount();
188   return NestingDetected;
189 }
190 
MatchWithRetain()191 bool BottomUpPtrState::MatchWithRetain() {
192   SetKnownPositiveRefCount();
193 
194   Sequence OldSeq = GetSeq();
195   switch (OldSeq) {
196   case S_Stop:
197   case S_Release:
198   case S_MovableRelease:
199   case S_Use:
200     // If OldSeq is not S_Use or OldSeq is S_Use and we are tracking an
201     // imprecise release, clear our reverse insertion points.
202     if (OldSeq != S_Use || IsTrackingImpreciseReleases())
203       ClearReverseInsertPts();
204   // FALL THROUGH
205   case S_CanRelease:
206     return true;
207   case S_None:
208     return false;
209   case S_Retain:
210     llvm_unreachable("bottom-up pointer in retain state!");
211   }
212   llvm_unreachable("Sequence unknown enum value");
213 }
214 
HandlePotentialAlterRefCount(Instruction * Inst,const Value * Ptr,ProvenanceAnalysis & PA,ARCInstKind Class)215 bool BottomUpPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
216                                                     const Value *Ptr,
217                                                     ProvenanceAnalysis &PA,
218                                                     ARCInstKind Class) {
219   Sequence S = GetSeq();
220 
221   // Check for possible releases.
222   if (!CanAlterRefCount(Inst, Ptr, PA, Class))
223     return false;
224 
225   DEBUG(dbgs() << "            CanAlterRefCount: Seq: " << S << "; " << *Ptr
226                << "\n");
227   switch (S) {
228   case S_Use:
229     SetSeq(S_CanRelease);
230     return true;
231   case S_CanRelease:
232   case S_Release:
233   case S_MovableRelease:
234   case S_Stop:
235   case S_None:
236     return false;
237   case S_Retain:
238     llvm_unreachable("bottom-up pointer in retain state!");
239   }
240   llvm_unreachable("Sequence unknown enum value");
241 }
242 
HandlePotentialUse(BasicBlock * BB,Instruction * Inst,const Value * Ptr,ProvenanceAnalysis & PA,ARCInstKind Class)243 void BottomUpPtrState::HandlePotentialUse(BasicBlock *BB, Instruction *Inst,
244                                           const Value *Ptr,
245                                           ProvenanceAnalysis &PA,
246                                           ARCInstKind Class) {
247   // Check for possible direct uses.
248   switch (GetSeq()) {
249   case S_Release:
250   case S_MovableRelease:
251     if (CanUse(Inst, Ptr, PA, Class)) {
252       DEBUG(dbgs() << "            CanUse: Seq: " << GetSeq() << "; " << *Ptr
253                    << "\n");
254       assert(!HasReverseInsertPts());
255       // If this is an invoke instruction, we're scanning it as part of
256       // one of its successor blocks, since we can't insert code after it
257       // in its own block, and we don't want to split critical edges.
258       if (isa<InvokeInst>(Inst))
259         InsertReverseInsertPt(BB->getFirstInsertionPt());
260       else
261         InsertReverseInsertPt(std::next(BasicBlock::iterator(Inst)));
262       SetSeq(S_Use);
263     } else if (Seq == S_Release && IsUser(Class)) {
264       DEBUG(dbgs() << "            PreciseReleaseUse: Seq: " << GetSeq() << "; "
265                    << *Ptr << "\n");
266       // Non-movable releases depend on any possible objc pointer use.
267       SetSeq(S_Stop);
268       assert(!HasReverseInsertPts());
269       // As above; handle invoke specially.
270       if (isa<InvokeInst>(Inst))
271         InsertReverseInsertPt(BB->getFirstInsertionPt());
272       else
273         InsertReverseInsertPt(std::next(BasicBlock::iterator(Inst)));
274     }
275     break;
276   case S_Stop:
277     if (CanUse(Inst, Ptr, PA, Class)) {
278       DEBUG(dbgs() << "            PreciseStopUse: Seq: " << GetSeq() << "; "
279                    << *Ptr << "\n");
280       SetSeq(S_Use);
281     }
282     break;
283   case S_CanRelease:
284   case S_Use:
285   case S_None:
286     break;
287   case S_Retain:
288     llvm_unreachable("bottom-up pointer in retain state!");
289   }
290 }
291 
292 //===----------------------------------------------------------------------===//
293 //                              TopDownPtrState
294 //===----------------------------------------------------------------------===//
295 
InitTopDown(ARCInstKind Kind,Instruction * I)296 bool TopDownPtrState::InitTopDown(ARCInstKind Kind, Instruction *I) {
297   bool NestingDetected = false;
298   // Don't do retain+release tracking for ARCInstKind::RetainRV, because
299   // it's
300   // better to let it remain as the first instruction after a call.
301   if (Kind != ARCInstKind::RetainRV) {
302     // If we see two retains in a row on the same pointer. If so, make
303     // a note, and we'll cicle back to revisit it after we've
304     // hopefully eliminated the second retain, which may allow us to
305     // eliminate the first retain too.
306     // Theoretically we could implement removal of nested retain+release
307     // pairs by making PtrState hold a stack of states, but this is
308     // simple and avoids adding overhead for the non-nested case.
309     if (GetSeq() == S_Retain)
310       NestingDetected = true;
311 
312     ResetSequenceProgress(S_Retain);
313     SetKnownSafe(HasKnownPositiveRefCount());
314     InsertCall(I);
315   }
316 
317   SetKnownPositiveRefCount();
318   return NestingDetected;
319 }
320 
MatchWithRelease(ARCMDKindCache & Cache,Instruction * Release)321 bool TopDownPtrState::MatchWithRelease(ARCMDKindCache &Cache,
322                                        Instruction *Release) {
323   ClearKnownPositiveRefCount();
324 
325   Sequence OldSeq = GetSeq();
326 
327   MDNode *ReleaseMetadata =
328       Release->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));
329 
330   switch (OldSeq) {
331   case S_Retain:
332   case S_CanRelease:
333     if (OldSeq == S_Retain || ReleaseMetadata != nullptr)
334       ClearReverseInsertPts();
335   // FALL THROUGH
336   case S_Use:
337     SetReleaseMetadata(ReleaseMetadata);
338     SetTailCallRelease(cast<CallInst>(Release)->isTailCall());
339     return true;
340   case S_None:
341     return false;
342   case S_Stop:
343   case S_Release:
344   case S_MovableRelease:
345     llvm_unreachable("top-down pointer in bottom up state!");
346   }
347   llvm_unreachable("Sequence unknown enum value");
348 }
349 
HandlePotentialAlterRefCount(Instruction * Inst,const Value * Ptr,ProvenanceAnalysis & PA,ARCInstKind Class)350 bool TopDownPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
351                                                    const Value *Ptr,
352                                                    ProvenanceAnalysis &PA,
353                                                    ARCInstKind Class) {
354   // Check for possible releases.
355   if (!CanAlterRefCount(Inst, Ptr, PA, Class))
356     return false;
357 
358   DEBUG(dbgs() << "            CanAlterRefCount: Seq: " << GetSeq() << "; " << *Ptr
359                << "\n");
360   ClearKnownPositiveRefCount();
361   switch (GetSeq()) {
362   case S_Retain:
363     SetSeq(S_CanRelease);
364     assert(!HasReverseInsertPts());
365     InsertReverseInsertPt(Inst);
366 
367     // One call can't cause a transition from S_Retain to S_CanRelease
368     // and S_CanRelease to S_Use. If we've made the first transition,
369     // we're done.
370     return true;
371   case S_Use:
372   case S_CanRelease:
373   case S_None:
374     return false;
375   case S_Stop:
376   case S_Release:
377   case S_MovableRelease:
378     llvm_unreachable("top-down pointer in release state!");
379   }
380   llvm_unreachable("covered switch is not covered!?");
381 }
382 
HandlePotentialUse(Instruction * Inst,const Value * Ptr,ProvenanceAnalysis & PA,ARCInstKind Class)383 void TopDownPtrState::HandlePotentialUse(Instruction *Inst, const Value *Ptr,
384                                          ProvenanceAnalysis &PA,
385                                          ARCInstKind Class) {
386   // Check for possible direct uses.
387   switch (GetSeq()) {
388   case S_CanRelease:
389     if (!CanUse(Inst, Ptr, PA, Class))
390       return;
391     DEBUG(dbgs() << "             CanUse: Seq: " << GetSeq() << "; " << *Ptr
392                  << "\n");
393     SetSeq(S_Use);
394     return;
395   case S_Retain:
396   case S_Use:
397   case S_None:
398     return;
399   case S_Stop:
400   case S_Release:
401   case S_MovableRelease:
402     llvm_unreachable("top-down pointer in release state!");
403   }
404 }
405