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