@@ -503,13 +503,18 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
503
503
// /
504
504
// / The derived class is expected to provide:
505
505
// / - A `Lattice` type.
506
- // / - `Lattice getInitialState()`
507
- // / - `Lattice join(Lattice, Lattice)`
508
- // / - `Lattice transfer(Lattice, const FactType&)` for relevant fact types.
506
+ // / - `const char *getAnalysisName() const`
507
+ // / - `Lattice getInitialState();` The initial state at the function entry.
508
+ // / - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths.
509
+ // / - `Lattice transfer(Lattice, const FactType&);` Defines how a single
510
+ // / lifetime-relevant `Fact` transforms the lattice state. Only overloads
511
+ // / for facts relevant to the analysis need to be implemented.
509
512
// /
510
513
// / \tparam Derived The CRTP derived class that implements the specific
511
514
// / analysis.
512
515
// / \tparam LatticeType The lattice type used by the analysis.
516
+ // / TODO: Maybe use the dataflow framework! The framework might need changes
517
+ // / to support the current comparison done at block-entry.
513
518
template <typename Derived, typename LatticeType> class DataflowAnalysis {
514
519
public:
515
520
using Lattice = LatticeType;
@@ -530,10 +535,12 @@ template <typename Derived, typename LatticeType> class DataflowAnalysis {
530
535
531
536
public:
532
537
void run () {
533
- Derived &d = static_cast <Derived &>(*this );
538
+ Derived &D = static_cast <Derived &>(*this );
539
+ llvm::TimeTraceScope Time (D.getAnalysisName ());
540
+
534
541
ForwardDataflowWorklist Worklist (Cfg, AC);
535
542
const CFGBlock *Entry = &Cfg.getEntry ();
536
- BlockEntryStates[Entry] = d .getInitialState ();
543
+ BlockEntryStates[Entry] = D .getInitialState ();
537
544
Worklist.enqueueBlock (Entry);
538
545
539
546
while (const CFGBlock *B = Worklist.dequeue ()) {
@@ -545,9 +552,11 @@ template <typename Derived, typename LatticeType> class DataflowAnalysis {
545
552
auto SuccIt = BlockEntryStates.find (Successor);
546
553
Lattice OldSuccEntryState = (SuccIt != BlockEntryStates.end ())
547
554
? SuccIt->second
548
- : d.getInitialState ();
549
- Lattice NewSuccEntryState = d.join (OldSuccEntryState, ExitState);
550
-
555
+ : D.getInitialState ();
556
+ Lattice NewSuccEntryState = D.join (OldSuccEntryState, ExitState);
557
+ // Enqueue the successor if its entry state has changed.
558
+ // TODO(opt): Consider changing 'join' to report a change if !=
559
+ // comparison is found expensive.
551
560
if (SuccIt == BlockEntryStates.end () ||
552
561
NewSuccEntryState != OldSuccEntryState) {
553
562
BlockEntryStates[Successor] = NewSuccEntryState;
@@ -565,7 +574,20 @@ template <typename Derived, typename LatticeType> class DataflowAnalysis {
565
574
return BlockExitStates.lookup (B);
566
575
}
567
576
577
+ void dump () const {
578
+ const Derived *D = static_cast <const Derived *>(this );
579
+ llvm::dbgs () << " ==========================================\n " ;
580
+ llvm::dbgs () << " " << D->getAnalysisName () << " results:\n " ;
581
+ llvm::dbgs () << " ==========================================\n " ;
582
+ const CFGBlock &B = Cfg.getExit ();
583
+ getExitState (&B).dump (llvm::dbgs ());
584
+ }
585
+
568
586
private:
587
+ // / Computes the exit state of a block by applying all its facts sequentially
588
+ // / to a given entry state.
589
+ // / TODO: We might need to store intermediate states per-fact in the block for
590
+ // / later analysis.
569
591
Lattice transferBlock (const CFGBlock *Block, Lattice EntryState) {
570
592
Lattice BlockState = EntryState;
571
593
for (const Fact *F : AllFacts.getFacts (Block)) {
@@ -618,24 +640,24 @@ struct LifetimeFactory {
618
640
}
619
641
};
620
642
621
- // / LifetimeLattice represents the state of our analysis at a given program
622
- // / point. It is an immutable object, and all operations produce a new
643
+ // / LoanPropagationLattice represents the state of our analysis at a given
644
+ // / program point. It is an immutable object, and all operations produce a new
623
645
// / instance rather than modifying the existing one.
624
- struct LifetimeLattice {
646
+ struct LoanPropagationLattice {
625
647
// / The map from an origin to the set of loans it contains.
626
648
// / The lattice has a finite height: An origin's loan set is bounded by the
627
649
// / total number of loans in the function.
628
650
// / TODO(opt): To reduce the lattice size, propagate origins of declarations,
629
651
// / not expressions, because expressions are not visible across blocks.
630
652
OriginLoanMap Origins = OriginLoanMap(nullptr );
631
653
632
- explicit LifetimeLattice (const OriginLoanMap &S) : Origins(S) {}
633
- LifetimeLattice () = default ;
654
+ explicit LoanPropagationLattice (const OriginLoanMap &S) : Origins(S) {}
655
+ LoanPropagationLattice () = default ;
634
656
635
- bool operator ==(const LifetimeLattice &Other) const {
657
+ bool operator ==(const LoanPropagationLattice &Other) const {
636
658
return Origins == Other.Origins ;
637
659
}
638
- bool operator !=(const LifetimeLattice &Other) const {
660
+ bool operator !=(const LoanPropagationLattice &Other) const {
639
661
return !(*this == Other);
640
662
}
641
663
@@ -653,7 +675,7 @@ struct LifetimeLattice {
653
675
};
654
676
655
677
class LoanPropagationAnalysis
656
- : public DataflowAnalysis<LoanPropagationAnalysis, LifetimeLattice > {
678
+ : public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice > {
657
679
658
680
LifetimeFactory &Factory;
659
681
@@ -662,49 +684,58 @@ class LoanPropagationAnalysis
662
684
LifetimeFactory &Factory)
663
685
: DataflowAnalysis(C, AC, F), Factory(Factory) {}
664
686
665
- // Make the base class's transfer overloads visible.
666
687
using DataflowAnalysis<LoanPropagationAnalysis, Lattice>::transfer;
667
688
689
+ const char *getAnalysisName () const { return " LoanPropagation" ; }
690
+
668
691
Lattice getInitialState () { return Lattice{}; }
669
692
670
- Lattice join (Lattice L1, Lattice L2) {
693
+ // / Computes the union of two lattices by performing a key-wise join of
694
+ // / their OriginLoanMaps.
695
+ // TODO(opt): This key-wise join is a performance bottleneck. A more
696
+ // efficient merge could be implemented using a Patricia Trie or HAMT
697
+ // instead of the current AVL-tree-based ImmutableMap.
698
+ // TODO(opt): Keep the state small by removing origins which become dead.
699
+ Lattice join (Lattice A, Lattice B) {
671
700
// / Merge the smaller map into the larger one ensuring we iterate over the
672
701
// / smaller map.
673
- if (L1 .Origins .getHeight () < L2 .Origins .getHeight ())
674
- std::swap (L1, L2 );
702
+ if (A .Origins .getHeight () < B .Origins .getHeight ())
703
+ std::swap (A, B );
675
704
676
- OriginLoanMap JoinedState = L1 .Origins ;
705
+ OriginLoanMap JoinedState = A .Origins ;
677
706
// For each origin in the other map, union its loan set with ours.
678
- for (const auto &Entry : L2 .Origins ) {
707
+ for (const auto &Entry : B .Origins ) {
679
708
OriginID OID = Entry.first ;
680
709
LoanSet OtherLoanSet = Entry.second ;
681
710
JoinedState = Factory.OriginMapFactory .add (
682
- JoinedState, OID, join (getLoans (L1 , OID), OtherLoanSet));
711
+ JoinedState, OID, join (getLoans (A , OID), OtherLoanSet));
683
712
}
684
713
return Lattice (JoinedState);
685
714
}
686
715
687
- LoanSet join (LoanSet S1 , LoanSet S2 ) {
688
- if (S1 .getHeight () < S2 .getHeight ())
689
- std::swap (S1, S2 );
690
- for (LoanID L : S2 )
691
- S1 = Factory.LoanSetFact .add (S1 , L);
692
- return S1 ;
716
+ LoanSet join (LoanSet A , LoanSet B ) {
717
+ if (A .getHeight () < B .getHeight ())
718
+ std::swap (A, B );
719
+ for (LoanID L : B )
720
+ A = Factory.LoanSetFact .add (A , L);
721
+ return A ;
693
722
}
694
723
695
- // Overloads for specific fact types this transferer cares about .
724
+ // / A new loan is issued to the origin. Old loans are erased .
696
725
Lattice transfer (Lattice In, const IssueFact &F) {
697
726
OriginID OID = F.getOriginID ();
698
727
LoanID LID = F.getLoanID ();
699
- return LifetimeLattice (Factory.OriginMapFactory .add (
728
+ return LoanPropagationLattice (Factory.OriginMapFactory .add (
700
729
In.Origins , OID, Factory.createLoanSet (LID)));
701
730
}
702
731
732
+ // / The destination origin's loan set is replaced by the source's.
733
+ // / This implicitly "resets" the old loans of the destination.
703
734
Lattice transfer (Lattice In, const AssignOriginFact &F) {
704
735
OriginID DestOID = F.getDestOriginID ();
705
736
OriginID SrcOID = F.getSrcOriginID ();
706
737
LoanSet SrcLoans = getLoans (In, SrcOID);
707
- return LifetimeLattice (
738
+ return LoanPropagationLattice (
708
739
Factory.OriginMapFactory .add (In.Origins , DestOID, SrcLoans));
709
740
}
710
741
@@ -715,65 +746,6 @@ class LoanPropagationAnalysis
715
746
return Factory.LoanSetFact .getEmptySet ();
716
747
}
717
748
};
718
-
719
- // ========================================================================= //
720
- // Expired Loans Analysis
721
- // ========================================================================= //
722
-
723
- // / The lattice for tracking expired loans. It is a set of loan IDs.
724
- struct ExpiredLattice {
725
- LoanSet Expired;
726
-
727
- ExpiredLattice () : Expired(nullptr ) {};
728
- explicit ExpiredLattice (LoanSet S) : Expired(S) {}
729
-
730
- bool operator ==(const ExpiredLattice &Other) const {
731
- return Expired == Other.Expired ;
732
- }
733
- bool operator !=(const ExpiredLattice &Other) const {
734
- return !(*this == Other);
735
- }
736
-
737
- void dump (llvm::raw_ostream &OS) const {
738
- OS << " ExpiredLattice State:\n " ;
739
- if (Expired.isEmpty ())
740
- OS << " <empty>\n " ;
741
- for (const LoanID &LID : Expired)
742
- OS << " Loan " << LID << " is expired\n " ;
743
- }
744
- };
745
-
746
- // / Transfer function for the expired loans analysis.
747
- class ExpiredLoansAnalysis
748
- : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice> {
749
-
750
- LoanSet::Factory &SetFactory;
751
-
752
- public:
753
- ExpiredLoansAnalysis (const CFG &C, AnalysisDeclContext &AC, FactManager &F,
754
- LoanSet::Factory &SF)
755
- : DataflowAnalysis(C, AC, F), SetFactory(SF) {}
756
-
757
- using DataflowAnalysis<ExpiredLoansAnalysis, Lattice>::transfer;
758
-
759
- Lattice getInitialState () { return Lattice (SetFactory.getEmptySet ()); }
760
-
761
- Lattice join (Lattice L1, Lattice L2) const {
762
- LoanSet JoinedSet = L1.Expired ;
763
- for (LoanID LID : L2.Expired )
764
- JoinedSet = SetFactory.add (JoinedSet, LID);
765
- return Lattice (JoinedSet);
766
- }
767
-
768
- // Overloads for specific fact types this transferer cares about.
769
- Lattice transfer (Lattice In, const ExpireFact &F) {
770
- return Lattice (SetFactory.add (In.Expired , F.getLoanID ()));
771
- }
772
-
773
- Lattice transfer (Lattice In, const IssueFact &F) {
774
- return Lattice (SetFactory.remove (In.Expired , F.getLoanID ()));
775
- }
776
- };
777
749
} // anonymous namespace
778
750
779
751
void runLifetimeSafetyAnalysis (const DeclContext &DC, const CFG &Cfg,
@@ -786,20 +758,18 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
786
758
FactGen.run ();
787
759
DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr.dump (Cfg, AC));
788
760
789
- // Run Loan Propagation Analysis
761
+ // / TODO(opt): Consider optimizing individual blocks before running the
762
+ // / dataflow analysis.
763
+ // / 1. Expression Origins: These are assigned once and read at most once,
764
+ // / forming simple chains. These chains can be compressed into a single
765
+ // / assignment.
766
+ // / 2. Block-Local Loans: Origins of expressions are never read by other
767
+ // / blocks; only Decls are visible. Therefore, loans in a block that
768
+ // / never reach an Origin associated with a Decl can be safely dropped by
769
+ // / the analysis.
790
770
LifetimeFactory LifetimeFact;
791
771
LoanPropagationAnalysis LoanPropagation (Cfg, AC, FactMgr, LifetimeFact);
792
772
LoanPropagation.run ();
793
- DEBUG_WITH_TYPE (
794
- " LifetimeDataflow" ,
795
- LoanPropagation.getExitState (&Cfg.getExit ()).dump (llvm::dbgs ()));
796
-
797
- // Run Expired Loans Analysis
798
- ExpiredLoansAnalysis ExpiredAnalysis (Cfg, AC, FactMgr,
799
- LifetimeFact.LoanSetFact );
800
- ExpiredAnalysis.run ();
801
- DEBUG_WITH_TYPE (
802
- " ExpiredLoans" ,
803
- ExpiredAnalysis.getExitState (&Cfg.getExit ()).dump (llvm::dbgs ()));
773
+ DEBUG_WITH_TYPE (" LifetimeLoanPropagation" , LoanPropagation.dump ());
804
774
}
805
775
} // namespace clang
0 commit comments