24
24
#include " llvm/Support/TimeProfiler.h"
25
25
#include < cstdint>
26
26
27
- namespace clang {
27
+ namespace clang ::lifetimes {
28
+ namespace internal {
28
29
namespace {
30
+ template <typename Tag>
31
+ inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
32
+ return OS << ID.Value ;
33
+ }
34
+ } // namespace
29
35
30
36
// / Represents the storage location being borrowed, e.g., a specific stack
31
37
// / variable.
@@ -36,32 +42,6 @@ struct AccessPath {
36
42
AccessPath (const clang::ValueDecl *D) : D(D) {}
37
43
};
38
44
39
- // / A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
40
- // / Used for giving ID to loans and origins.
41
- template <typename Tag> struct ID {
42
- uint32_t Value = 0 ;
43
-
44
- bool operator ==(const ID<Tag> &Other) const { return Value == Other.Value ; }
45
- bool operator !=(const ID<Tag> &Other) const { return !(*this == Other); }
46
- bool operator <(const ID<Tag> &Other) const { return Value < Other.Value ; }
47
- ID<Tag> operator ++(int ) {
48
- ID<Tag> Tmp = *this ;
49
- ++Value;
50
- return Tmp;
51
- }
52
- void Profile (llvm::FoldingSetNodeID &IDBuilder) const {
53
- IDBuilder.AddInteger (Value);
54
- }
55
- };
56
-
57
- template <typename Tag>
58
- inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
59
- return OS << ID.Value ;
60
- }
61
-
62
- using LoanID = ID<struct LoanTag >;
63
- using OriginID = ID<struct OriginTag >;
64
-
65
45
// / Information about a single borrow, or "Loan". A loan is created when a
66
46
// / reference or pointer is created.
67
47
struct Loan {
@@ -223,7 +203,9 @@ class Fact {
223
203
// / An origin is propagated from a source to a destination (e.g., p = q).
224
204
AssignOrigin,
225
205
// / An origin escapes the function by flowing into the return value.
226
- ReturnOfOrigin
206
+ ReturnOfOrigin,
207
+ // / A marker for a specific point in the code, for testing.
208
+ TestPoint,
227
209
};
228
210
229
211
private:
@@ -310,6 +292,24 @@ class ReturnOfOriginFact : public Fact {
310
292
}
311
293
};
312
294
295
+ // / A dummy-fact used to mark a specific point in the code for testing.
296
+ // / It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
297
+ class TestPointFact : public Fact {
298
+ std::string Annotation;
299
+
300
+ public:
301
+ static bool classof (const Fact *F) { return F->getKind () == Kind::TestPoint; }
302
+
303
+ explicit TestPointFact (std::string Annotation)
304
+ : Fact(Kind::TestPoint), Annotation(std::move(Annotation)) {}
305
+
306
+ const std::string &getAnnotation () const { return Annotation; }
307
+
308
+ void dump (llvm::raw_ostream &OS) const override {
309
+ OS << " TestPoint (Annotation: \" " << getAnnotation () << " \" )\n " ;
310
+ }
311
+ };
312
+
313
313
class FactManager {
314
314
public:
315
315
llvm::ArrayRef<const Fact *> getFacts (const CFGBlock *B) const {
@@ -363,6 +363,7 @@ class FactManager {
363
363
};
364
364
365
365
class FactGenerator : public ConstStmtVisitor <FactGenerator> {
366
+ using Base = ConstStmtVisitor<FactGenerator>;
366
367
367
368
public:
368
369
FactGenerator (FactManager &FactMgr, AnalysisDeclContext &AC)
@@ -458,6 +459,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
458
459
}
459
460
}
460
461
462
+ void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
463
+ // Check if this is a test point marker. If so, we are done with this
464
+ // expression.
465
+ if (VisitTestPoint (FCE))
466
+ return ;
467
+ // Visit as normal otherwise.
468
+ Base::VisitCXXFunctionalCastExpr (FCE);
469
+ }
470
+
461
471
private:
462
472
// Check if a type has an origin.
463
473
bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
@@ -491,6 +501,27 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
491
501
}
492
502
}
493
503
504
+ // / Checks if the expression is a `void("__lifetime_test_point_...")` cast.
505
+ // / If so, creates a `TestPointFact` and returns true.
506
+ bool VisitTestPoint (const CXXFunctionalCastExpr *FCE) {
507
+ if (!FCE->getType ()->isVoidType ())
508
+ return false ;
509
+
510
+ const auto *SubExpr = FCE->getSubExpr ()->IgnoreParenImpCasts ();
511
+ if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
512
+ llvm::StringRef LiteralValue = SL->getString ();
513
+ const std::string Prefix = " __lifetime_test_point_" ;
514
+
515
+ if (LiteralValue.starts_with (Prefix)) {
516
+ std::string Annotation = LiteralValue.drop_front (Prefix.length ()).str ();
517
+ CurrentBlockFacts.push_back (
518
+ FactMgr.createFact <TestPointFact>(Annotation));
519
+ return true ;
520
+ }
521
+ }
522
+ return false ;
523
+ }
524
+
494
525
FactManager &FactMgr;
495
526
AnalysisDeclContext &AC;
496
527
llvm::SmallVector<Fact *> CurrentBlockFacts;
@@ -637,6 +668,8 @@ class DataflowAnalysis {
637
668
return D->transfer (In, *F->getAs <AssignOriginFact>());
638
669
case Fact::Kind::ReturnOfOrigin:
639
670
return D->transfer (In, *F->getAs <ReturnOfOriginFact>());
671
+ case Fact::Kind::TestPoint:
672
+ return D->transfer (In, *F->getAs <TestPointFact>());
640
673
}
641
674
llvm_unreachable (" Unknown fact kind" );
642
675
}
@@ -646,14 +679,16 @@ class DataflowAnalysis {
646
679
Lattice transfer (Lattice In, const ExpireFact &) { return In; }
647
680
Lattice transfer (Lattice In, const AssignOriginFact &) { return In; }
648
681
Lattice transfer (Lattice In, const ReturnOfOriginFact &) { return In; }
682
+ Lattice transfer (Lattice In, const TestPointFact &) { return In; }
649
683
};
650
684
651
685
namespace utils {
652
686
653
687
// / Computes the union of two ImmutableSets.
654
688
template <typename T>
655
- llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A, llvm::ImmutableSet<T> B,
656
- typename llvm::ImmutableSet<T>::Factory &F) {
689
+ static llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A,
690
+ llvm::ImmutableSet<T> B,
691
+ typename llvm::ImmutableSet<T>::Factory &F) {
657
692
if (A.getHeight () < B.getHeight ())
658
693
std::swap (A, B);
659
694
for (const T &E : B)
@@ -666,7 +701,7 @@ llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A, llvm::ImmutableSet<T> B,
666
701
// efficient merge could be implemented using a Patricia Trie or HAMT
667
702
// instead of the current AVL-tree-based ImmutableMap.
668
703
template <typename K, typename V, typename Joiner>
669
- llvm::ImmutableMap<K, V>
704
+ static llvm::ImmutableMap<K, V>
670
705
join (llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
671
706
typename llvm::ImmutableMap<K, V>::Factory &F, Joiner joinValues) {
672
707
if (A.getHeight () < B.getHeight ())
@@ -690,10 +725,6 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
690
725
// Loan Propagation Analysis
691
726
// ========================================================================= //
692
727
693
- // Using LLVM's immutable collections is efficient for dataflow analysis
694
- // as it avoids deep copies during state transitions.
695
- // TODO(opt): Consider using a bitset to represent the set of loans.
696
- using LoanSet = llvm::ImmutableSet<LoanID>;
697
728
using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
698
729
699
730
// / An object to hold the factories for immutable collections, ensuring
@@ -807,17 +838,27 @@ class LoanPropagationAnalysis
807
838
// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
808
839
// - Using the above three to perform the final error reporting.
809
840
// ========================================================================= //
810
- } // anonymous namespace
811
841
812
- void runLifetimeSafetyAnalysis (const DeclContext &DC, const CFG &Cfg,
813
- AnalysisDeclContext &AC) {
842
+ // ========================================================================= //
843
+ // LifetimeSafetyAnalysis Class Implementation
844
+ // ========================================================================= //
845
+
846
+ LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis () = default ;
847
+
848
+ LifetimeSafetyAnalysis::LifetimeSafetyAnalysis (AnalysisDeclContext &AC)
849
+ : AC(AC), Factory(std::make_unique<LifetimeFactory>()),
850
+ FactMgr (std::make_unique<FactManager>()) {}
851
+
852
+ void LifetimeSafetyAnalysis::run () {
814
853
llvm::TimeTraceScope TimeProfile (" LifetimeSafetyAnalysis" );
854
+
855
+ const CFG &Cfg = *AC.getCFG ();
815
856
DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
816
857
/* ShowColors=*/ true ));
817
- FactManager FactMgr;
818
- FactGenerator FactGen (FactMgr, AC);
858
+
859
+ FactGenerator FactGen (* FactMgr, AC);
819
860
FactGen.run ();
820
- DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr. dump (Cfg, AC));
861
+ DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr-> dump (Cfg, AC));
821
862
822
863
// / TODO(opt): Consider optimizing individual blocks before running the
823
864
// / dataflow analysis.
@@ -828,9 +869,49 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
828
869
// / blocks; only Decls are visible. Therefore, loans in a block that
829
870
// / never reach an Origin associated with a Decl can be safely dropped by
830
871
// / the analysis.
831
- LifetimeFactory Factory;
832
- LoanPropagationAnalysis LoanPropagation (Cfg, AC, FactMgr, Factory);
833
- LoanPropagation.run ();
834
- DEBUG_WITH_TYPE (" LifetimeLoanPropagation" , LoanPropagation.dump ());
872
+ LoanPropagation =
873
+ std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
874
+ LoanPropagation->run ();
875
+ }
876
+
877
+ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint (OriginID OID,
878
+ ProgramPoint PP) const {
879
+ assert (LoanPropagation && " Analysis has not been run." );
880
+ return LoanPropagation->getLoans (OID, PP);
881
+ }
882
+
883
+ std::optional<OriginID>
884
+ LifetimeSafetyAnalysis::getOriginIDForDecl (const ValueDecl *D) const {
885
+ assert (FactMgr && " FactManager not initialized" );
886
+ // This assumes the OriginManager's `get` can find an existing origin.
887
+ // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
888
+ // in a const-query context if that becomes an issue.
889
+ return FactMgr->getOriginMgr ().get (*D);
890
+ }
891
+
892
+ std::vector<LoanID>
893
+ LifetimeSafetyAnalysis::getLoanIDForVar (const VarDecl *VD) const {
894
+ assert (FactMgr && " FactManager not initialized" );
895
+ std::vector<LoanID> Result;
896
+ for (const Loan &L : FactMgr->getLoanMgr ().getLoans ())
897
+ if (L.Path .D == VD)
898
+ Result.push_back (L.ID );
899
+ return Result;
900
+ }
901
+
902
+ llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints () const {
903
+ assert (FactMgr && " FactManager not initialized" );
904
+ llvm::StringMap<ProgramPoint> AnnotationToPointMap;
905
+ for (const CFGBlock *Block : *AC.getCFG ())
906
+ for (const Fact *F : FactMgr->getFacts (Block))
907
+ if (const auto *TPF = F->getAs <TestPointFact>())
908
+ AnnotationToPointMap[TPF->getAnnotation ()] = F;
909
+ return AnnotationToPointMap;
910
+ }
911
+ } // namespace internal
912
+
913
+ void runLifetimeSafetyAnalysis (AnalysisDeclContext &AC) {
914
+ internal::LifetimeSafetyAnalysis Analysis (AC);
915
+ Analysis.run ();
835
916
}
836
- } // namespace clang
917
+ } // namespace clang::lifetimes
0 commit comments