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